mirror of
https://github.com/HaitiData/haitidata.git
synced 2026-10-27 01:45:40 +00:00
commit
aa0f45a465
58 files changed
+5077
-8421
No files matched your search
@@ -65,3 +65,6 @@ geoserver/
|
||||
uploaded/
|
||||
development.db
|
||||
development.db-journal
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Whitespace-only changes.
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Django settings for charts project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.8.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.8/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
from geonode.settings import *
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SITENAME = 'charts-prj'
|
||||
|
||||
LOCAL_ROOT = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '*u(5zzp_wjj!^!w4$h@e#jv!*y02z17lb8gu-l)j+*79p^r8%('
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = INSTALLED_APPS + (
|
||||
'charts_app',
|
||||
'wfs_harvest'
|
||||
)
|
||||
|
||||
WSGI_APPLICATION = 'charts-prj.wsgi.application'
|
||||
|
||||
# Additional directories which hold static files
|
||||
STATICFILES_DIRS.append(
|
||||
os.path.join(LOCAL_ROOT, "static"),
|
||||
)
|
||||
|
||||
# Location of url mappings
|
||||
ROOT_URLCONF = 'charts-prj.urls'
|
||||
|
||||
# Location of locale files
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(LOCAL_ROOT, 'locale'),
|
||||
) + LOCALE_PATHS
|
||||
|
||||
TEMPLATES[0]['DIRS'].insert(0, os.path.join(LOCAL_ROOT, "templates"))
|
||||
|
||||
MAX_CSV_RECORDS = 20000
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
PROXY_ALLOWED_HOSTS = ['*']
|
||||
@@ -0,0 +1,8 @@
|
||||
This directory is used to store static assets for your project. User media files
|
||||
(FileFields/ImageFields) are not stored here.
|
||||
|
||||
The convention for this directory is:
|
||||
|
||||
* css/ — stores CSS files
|
||||
* js/ — stores Javascript files
|
||||
* img/ — stores image files
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,24 @@
|
||||
{% load i18n %}
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#info" data-toggle="tab"><i class="fa fa-info-circle"></i>{% trans "Info" %}</a></li>
|
||||
{% if resource.polymorphic_ctype.name == 'layer' %}
|
||||
<li><a href="#attributes" data-toggle="tab"><i class="fa fa-bars"></i>{% trans "Attributes" %}</a></li>
|
||||
{% endif %}
|
||||
{% if resource.is_mosaic %}
|
||||
<li><a href="#granules" data-toggle="tab"><i class="fa fa-bars"></i>{% trans "Granules" %}</a></li>
|
||||
{% endif %}
|
||||
{% if resource.service_type == 'WFS' %}
|
||||
<li><a href="#charts" data-toggle="tab"><i class="fa fa-charts"></i>{% trans "Charts" %}</a></li>
|
||||
{% endif %}
|
||||
{% if SOCIAL_BUTTONS %}
|
||||
<li><a href="#share" data-toggle="tab"><i class="fa fa-share"></i>{% trans "Share" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a href="#rate" data-toggle="tab"><i class="fa fa-star"></i>{% trans "Ratings" %}</a></li>
|
||||
<li><a href="#comments" data-toggle="tab"><i class="fa fa-comment-o"></i> {% trans "Comments" %}</a></li>
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
<li><a href="#geogig" data-toggle="tab"><i class="fa fa-code-fork"></i> {% trans "GeoGig" %}</a></li>
|
||||
{% endif %}
|
||||
{% if EXIF_ENABLED and resource.polymorphic_ctype.name == 'document' and exif_data %}
|
||||
<li><a href="#exif" data-toggle="tab"><i class="fa fa-photo"></i> {% trans "Exif" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
@@ -0,0 +1,4 @@
|
||||
{% extends "site_base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% block extra_head %}
|
||||
<link href="{{ STATIC_URL }}css/site_base.css" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
{% block extra_tab %}
|
||||
{% comment %}
|
||||
Add Tab for Third Party Apps
|
||||
<li>
|
||||
<a href="{{ PROJECT_ROOT }}app">App</a>
|
||||
</li>
|
||||
{% endcomment %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends 'index.html' %}
|
||||
{% load i18n %}
|
||||
{% comment %}
|
||||
This is where you can override the hero area block. You can simply modify the content below or replace it wholesale to meet your own needs.
|
||||
{% endcomment %}
|
||||
{% block hero %}
|
||||
<div class="jumbotron">
|
||||
<div class="container">
|
||||
<h1>{% trans "Welcome to the charts GeoNode" %}</h1>
|
||||
<p>{% blocktrans %}GeoNode is an open source platform for sharing geospatial data and maps.{% endblocktrans %}</p>
|
||||
<p>{% blocktrans %}If you have any questions about the software or service, join our <a href="https://groups.google.com/d/forum/geonode-users">mailing list</a>.{% endblocktrans %}</p>
|
||||
<span>{% trans 'Need help?' %} <a href="http://docs.geonode.org/en/latest/tutorials/users/quickstart/index.html">{% trans 'Getting Started' %}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Django settings for pippo project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.8.7.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.8/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '5i81&t!#h$4ie!-kg$&@5b-1j*)d9@(6)8g8o4kg*xw3i2e=@t'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'charts-prj.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'charts-prj.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.8/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
ANONYMOUS_USER_ID = 1
|
||||
|
||||
LOCAL_MEDIA_URL = ''
|
||||
API_INCLUDE_REGIONS_COUNT = ''
|
||||
HAYSTACK_SEARCH = ''
|
||||
RESOURCE_PUBLISHING = ''
|
||||
@@ -0,0 +1,13 @@
|
||||
from django.conf.urls import patterns, url
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from geonode.urls import *
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^/?$',
|
||||
TemplateView.as_view(template_name='site_index.html'),
|
||||
name='home'),
|
||||
) + urlpatterns + [
|
||||
url(r'^chart/', include('charts_app.urls')),
|
||||
url(r'^table/', include('wfs_harvest.urls')),
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
WSGI config for charts project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "charts-prj.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
||||
Whitespace-only changes.
@@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Chart
|
||||
|
||||
|
||||
class ChartAdmin(admin.ModelAdmin):
|
||||
list_display = ['id', 'title']
|
||||
|
||||
|
||||
admin.site.register(Chart, ChartAdmin)
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('layers', '24_to_26'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Chart',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('title', models.CharField(max_length=128, blank=True)),
|
||||
('category', models.CharField(max_length=200)),
|
||||
('quantity', models.CharField(max_length=200)),
|
||||
('type', models.SmallIntegerField(default=0, choices=[(0, b'Bar chart'), (1, b'Pie chart'), (2, b'Donut chart')])),
|
||||
('aggr_type', models.SmallIntegerField(default=3, choices=[(0, b'Sum'), (1, b'Mean'), (2, b'Category count'), (3, b'Max'), (4, b'Min')])),
|
||||
('abstract', models.TextField(blank=True)),
|
||||
('layer', models.ForeignKey(to='layers.Layer')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
import charts_app.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('charts_app', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='chart',
|
||||
name='created_by',
|
||||
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='chart',
|
||||
name='layer',
|
||||
field=models.ForeignKey(to='layers.Layer', validators=[charts_app.models.validate_wfs]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='chart',
|
||||
name='type',
|
||||
field=models.SmallIntegerField(default=0, choices=[(0, b'Bar chart'), (1, b'Pie chart'), (2, b'Donut chart'), (3, b'Line chart')]),
|
||||
),
|
||||
]
|
||||
Whitespace-only changes.
@@ -0,0 +1,80 @@
|
||||
from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.forms import ModelForm, ValidationError as VE
|
||||
|
||||
from geonode.layers.models import Layer
|
||||
|
||||
from wfs_harvest.utils import get_featno, get_fields
|
||||
|
||||
|
||||
def validate_wfs(value):
|
||||
lyr = Layer.objects.get(pk=value)
|
||||
if lyr.service_type != 'WFS':
|
||||
raise ValidationError(_('%(lyr_title)s is not a WFS layer'),
|
||||
code='not_a_wfs',
|
||||
params={'lyr_title': lyr.title})
|
||||
|
||||
feat_no = get_featno(value)
|
||||
if feat_no > settings.MAX_CSV_RECORDS:
|
||||
raise ValidationError(_('%(lyr_title)s has too many features; '
|
||||
'chart functionality is not available '
|
||||
'for very large datasets'),
|
||||
code='too_many_records',
|
||||
params={'lyr_title': lyr.title})
|
||||
|
||||
|
||||
class Chart(models.Model):
|
||||
CHART_TYPES = (
|
||||
(0, 'Bar chart'),
|
||||
(1, 'Pie chart'),
|
||||
(2, 'Donut chart'),
|
||||
(3, 'Line chart')
|
||||
)
|
||||
|
||||
AGGREGATION_TYPES = (
|
||||
(0, 'Sum'),
|
||||
(1, 'Mean'),
|
||||
(2, 'Category count'),
|
||||
(3, 'Max'),
|
||||
(4, 'Min'),
|
||||
)
|
||||
|
||||
layer = models.ForeignKey(Layer, validators=[validate_wfs])
|
||||
title = models.CharField(max_length=128, blank=True)
|
||||
category = models.CharField(max_length=200)
|
||||
quantity = models.CharField(max_length=200)
|
||||
type = models.SmallIntegerField(choices=CHART_TYPES, default=0)
|
||||
aggr_type = models.SmallIntegerField(choices=AGGREGATION_TYPES, default=3)
|
||||
abstract = models.TextField(blank=True)
|
||||
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null = True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('chart_detail', kwargs={'pk': self.pk})
|
||||
|
||||
class ChartForm(ModelForm):
|
||||
def clean(self):
|
||||
cleaned_data = super(ChartForm, self).clean()
|
||||
layer = cleaned_data.get('layer')
|
||||
category = cleaned_data.get('category')
|
||||
quantity = cleaned_data.get('quantity')
|
||||
|
||||
categories, quantities = get_fields(layer)
|
||||
|
||||
if category not in categories:
|
||||
lyr = Layer.objects.get(pk=layer)
|
||||
raise VE(_('%(lyr_title)s does not contain the field %(category)s'),
|
||||
code='non_extant_category_field',
|
||||
params={'lyr_title': lyr.title, 'category': category})
|
||||
|
||||
if quantity not in quantities:
|
||||
lyr = Layer.objects.get(pk=layer)
|
||||
raise VE(_('%(lyr_title)s does not contain the field %(quantity)s'),
|
||||
code='non_extant_quantity_field',
|
||||
params={'lyr_title': lyr.title, 'quantity': quantity})
|
||||
|
||||
class Meta:
|
||||
model = Chart
|
||||
fields = '__all__'
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Donut Chart v1</title>
|
||||
<h1>Simple Donut Chart v1</h1>
|
||||
<h2>Zero values excluded</h2>
|
||||
<p>D3js version 4, use canvas</p>
|
||||
<canvas width="960" height="500"></canvas>
|
||||
<script src="//d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
|
||||
var canvas = document.querySelector("canvas"),
|
||||
context = canvas.getContext("2d");
|
||||
|
||||
var width = canvas.width,
|
||||
height = canvas.height,
|
||||
radius = Math.min(width, height) / 2;
|
||||
|
||||
var colors = ["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00", "#800000", "#8E388E",
|
||||
"#7171C6", "#7D9EC0", "#388E8E", "#71C671", "#8E8E38", "#C5C1AA", "#C67171"];
|
||||
|
||||
var arc = d3.arc()
|
||||
.outerRadius(radius - 10)
|
||||
.innerRadius(radius - 70)
|
||||
.context(context);
|
||||
|
||||
var labelArc = d3.arc()
|
||||
.outerRadius(radius - 40)
|
||||
.innerRadius(radius - 40)
|
||||
.context(context);
|
||||
|
||||
var pie = d3.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.area; });
|
||||
|
||||
context.translate(width / 2, height / 2);
|
||||
|
||||
d3.csv("{% url "get_csv" %}", function(d) {
|
||||
d.area = +d.area;
|
||||
if (d.area !== 0) {
|
||||
return d;}
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
var arcs = pie(data);
|
||||
|
||||
arcs.forEach(function(d, i) {
|
||||
context.beginPath();
|
||||
arc(d);
|
||||
context.fillStyle = colors[i];
|
||||
context.fill();
|
||||
});
|
||||
|
||||
context.beginPath();
|
||||
arcs.forEach(arc);
|
||||
context.strokeStyle = "#fff";
|
||||
context.stroke();
|
||||
|
||||
context.textAlign = "center";
|
||||
context.textBaseline = "middle";
|
||||
context.fillStyle = "#000";
|
||||
arcs.forEach(function(d) {
|
||||
var c = labelArc.centroid(d);
|
||||
context.fillText(d.data.name, c[0], c[1]);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<body></body>
|
||||
@@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Donut Chart v2</title>
|
||||
<h1>Simple Donut Chart v2</h1>
|
||||
<h2>Zero values excluded, label with ticks</h2>
|
||||
<p>D3js version 3, use svg</p>
|
||||
<style>
|
||||
|
||||
body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
width: 960px;
|
||||
height: 500px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
path.slice{
|
||||
stroke-width:2px;
|
||||
}
|
||||
|
||||
polyline{
|
||||
opacity: .3;
|
||||
stroke: black;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<script src="http://d3js.org/d3.v3.min.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
var svg = d3.select("body")
|
||||
.append("svg")
|
||||
.append("g")
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "slices");
|
||||
svg.append("g")
|
||||
.attr("class", "labels");
|
||||
svg.append("g")
|
||||
.attr("class", "lines");
|
||||
|
||||
var width = 960,
|
||||
height = 500,
|
||||
radius = Math.min(width, height) / 2;
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function(d) {
|
||||
return d.area;
|
||||
});
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.outerRadius(radius * 0.8)
|
||||
.innerRadius(radius * 0.4);
|
||||
|
||||
var outerArc = d3.svg.arc()
|
||||
.innerRadius(radius * 0.9)
|
||||
.outerRadius(radius * 0.9);
|
||||
|
||||
d3.csv("{% url "get_csv" %}", function(d) {
|
||||
d.area = +d.area;
|
||||
if (d.area !== 0) {
|
||||
return d;}
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var key = function(d){ return d.data.name};
|
||||
|
||||
var color = d3.scale.ordinal()
|
||||
.domain([key])
|
||||
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
|
||||
|
||||
|
||||
var slice = svg.select(".slices").selectAll("path.slice")
|
||||
.data(pie(data), key);
|
||||
|
||||
slice.enter()
|
||||
.insert("path")
|
||||
.style("fill", function(d) { return color(d.data.area); })
|
||||
.attr("class", "slice");
|
||||
|
||||
slice
|
||||
.transition().duration(1000)
|
||||
.attrTween("d", function(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
return arc(interpolate(t));
|
||||
};
|
||||
})
|
||||
|
||||
|
||||
|
||||
/* ------- TEXT LABELS -------*/
|
||||
|
||||
var text = svg.select(".labels").selectAll("text")
|
||||
.data(pie(data), key);
|
||||
|
||||
text.enter()
|
||||
.append("text")
|
||||
.attr("dy", ".35em")
|
||||
.text(function(d) {
|
||||
return d.data.name + ' - ' +d.data.area; ;
|
||||
});
|
||||
|
||||
function midAngle(d){
|
||||
return d.startAngle + (d.endAngle - d.startAngle)/2;
|
||||
}
|
||||
|
||||
text.transition().duration(1000)
|
||||
.attrTween("transform", function(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
var d2 = interpolate(t);
|
||||
var pos = outerArc.centroid(d2);
|
||||
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
|
||||
return "translate("+ pos +")";
|
||||
};
|
||||
})
|
||||
.styleTween("text-anchor", function(d){
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
var d2 = interpolate(t);
|
||||
return midAngle(d2) < Math.PI ? "start":"end";
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
/* ------- SLICE TO TEXT POLYLINES -------*/
|
||||
|
||||
var polyline = svg.select(".lines").selectAll("polyline")
|
||||
.data(pie(data), key);
|
||||
|
||||
polyline.enter()
|
||||
.append("polyline");
|
||||
|
||||
polyline.transition().duration(1000)
|
||||
.attrTween("points", function(d){
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t) {
|
||||
var d2 = interpolate(t);
|
||||
var pos = outerArc.centroid(d2);
|
||||
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
|
||||
return [arc.centroid(d2), outerArc.centroid(d2), pos];
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Pie Chart v1</title>
|
||||
<h1>Simple Pie Chart v1</h1>
|
||||
<h2>All data</h2>
|
||||
<p>D3js version 4, use svg</p>
|
||||
<style>
|
||||
|
||||
.arc text {
|
||||
font: 10px sans-serif;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
<svg width="960" height="500"></svg>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
|
||||
var svg = d3.select("svg"),
|
||||
width = +svg.attr("width"),
|
||||
height = +svg.attr("height"),
|
||||
radius = Math.min(width, height) / 2,
|
||||
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
|
||||
|
||||
var pie = d3.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.area; });
|
||||
|
||||
var path = d3.arc()
|
||||
.outerRadius(radius - 10)
|
||||
.innerRadius(0);
|
||||
|
||||
var label = d3.arc()
|
||||
.outerRadius(radius - 40)
|
||||
.innerRadius(radius - 40);
|
||||
|
||||
|
||||
d3.csv("{% url "get_csv" %}", function(d) {
|
||||
d.area = +d.area;
|
||||
return d;
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
var arc = g.selectAll(".arc")
|
||||
.data(pie(data))
|
||||
.enter().append("g")
|
||||
.attr("class", "arc");
|
||||
|
||||
arc.append("path")
|
||||
.attr("d", path)
|
||||
.attr("fill", function(d) { return color(d.data.name); });
|
||||
|
||||
arc.append("text")
|
||||
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
|
||||
.attr("dy", "0.35em")
|
||||
.text(function(d) { return d.data.name; });
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Pie Chart v2</title>
|
||||
<h1>Simple Pie Chart v2</h1>
|
||||
<h2>Zero values excluded</h2>
|
||||
<p>D3js version 4, use svg</p>
|
||||
<style>
|
||||
|
||||
.arc text {
|
||||
font: 10px sans-serif;
|
||||
text-anchor: middle;
|
||||
}
|
||||
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
<svg width="960" height="500"></svg>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
|
||||
|
||||
var svg = d3.select("svg"),
|
||||
width = +svg.attr("width"),
|
||||
height = +svg.attr("height"),
|
||||
radius = Math.min(width, height) / 2,
|
||||
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
var color = d3.scaleOrdinal(d3.schemeCategory20);
|
||||
|
||||
var pie = d3.pie()
|
||||
.sort(null)
|
||||
.value(function(d) { return d.{{ quantity }}; });
|
||||
|
||||
var path = d3.arc()
|
||||
.outerRadius(radius - 10)
|
||||
.innerRadius(0);
|
||||
|
||||
var label = d3.arc()
|
||||
.outerRadius(radius - 40)
|
||||
.innerRadius(radius - 40);
|
||||
|
||||
|
||||
d3.csv("/table?lyrname={{ lyrname }}&category={{ category }}&quantity={{ quantity }}", function(d) {
|
||||
d.area = +d.area;
|
||||
if (d.area !== 0) {
|
||||
return d;}
|
||||
}, function(error, data) {
|
||||
if (error) throw error;
|
||||
|
||||
var arc = g.selectAll(".arc")
|
||||
.data(pie(data))
|
||||
.enter().append("g")
|
||||
.attr("class", "arc");
|
||||
|
||||
arc.append("path")
|
||||
.attr("d", path)
|
||||
.attr("fill", function(d) { return color(d.data.{{ category }}); });
|
||||
|
||||
arc.append("text")
|
||||
.attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; })
|
||||
.attr("dy", "0.35em")
|
||||
.text(function(d) { return d.data.{{ category }}; });
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,10 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [url(r'^(?P<pk>[0-9]+)/$', views.ChartDetail.as_view(), name='chart_detail'),
|
||||
url(r'^add/(?P<layer_id>[0-9]+)/$', views.ChartCreate.as_view(), name='chart_add'),
|
||||
url(r'^(?P<pk>[0-9]+)/update/$', views.ChartUpdate.as_view(), name='chart_update'),
|
||||
url(r'^(?P<pk>[0-9]+)/delete/$', views.ChartDelete.as_view(), name='chart_delete'),
|
||||
url(r'^$', views.ChartList.as_view(), name='chart_list')
|
||||
]
|
||||
@@ -0,0 +1,105 @@
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
from django.views.generic.list import ListView
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
|
||||
from geonode.layers.models import Layer
|
||||
|
||||
from .models import Chart, ChartForm
|
||||
from wfs_harvest.utils import get_fields
|
||||
|
||||
|
||||
class LoginRequiredMixin(object):
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
|
||||
return login_required(view)
|
||||
|
||||
|
||||
class ChartDetail(DetailView):
|
||||
model = Chart
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ChartDetail, self).get_object(queryset=queryset)
|
||||
lyr_obj = Layer.objects.get(pk=obj.layer)
|
||||
if not self.request.user.has_perm('download_resourcebase', lyr_obj.get_self_resource()):
|
||||
raise PermissionDenied
|
||||
return obj
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ChartDetail, self).get_context_data(**kwargs)
|
||||
return context
|
||||
|
||||
|
||||
class ChartCreate(LoginRequiredMixin, CreateView):
|
||||
form_class = ChartForm
|
||||
template_name_suffix = 'charts_app/chart_update_form.html'
|
||||
|
||||
def get_initial(self):
|
||||
layer_id = self.kwargs['layer_id']
|
||||
initial = self.initial.copy()
|
||||
initial['layer'] = layer_id
|
||||
return initial
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
layer_id = self.kwargs['layer_id']
|
||||
layer = Layer.objects.get(pk=layer_id)
|
||||
if not self.request.user.has_perm('download_resourcebase', layer.get_self_resource()):
|
||||
raise PermissionDenied
|
||||
fieldnames, num_fieldnames = get_fields(layer_id)
|
||||
ctx = super(ChartCreate, self).get_context_data(**kwargs)
|
||||
ctx['fieldnames'] = fieldnames
|
||||
ctx['num_fieldnames'] = num_fieldnames
|
||||
ctx['layer'] = layer
|
||||
return ctx
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.has_perm('download_resourcebase', form.instance.layer.get_self_resource()):
|
||||
raise PermissionDenied
|
||||
form.instance.created_by = self.request.user
|
||||
return super(ChartCreate, self).form_valid(form)
|
||||
|
||||
|
||||
class ChartUpdate(LoginRequiredMixin, UpdateView):
|
||||
model = Chart
|
||||
fields = '__all__'
|
||||
template_name = 'charts_app/chart_update_form.html'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ChartUpdate, self).get_object(queryset=queryset)
|
||||
is_chart_owner = (self.request.user == obj.created_by)
|
||||
lyr_obj = Layer.objects.get(pk=obj.layer)
|
||||
is_lyr_owner = (self.request.user == lyr_obj.owner)
|
||||
if not (self.request.user.is_superuser or is_chart_owner or is_lyr_owner):
|
||||
raise PermissionDenied
|
||||
return obj
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.created_by = self.request.user
|
||||
return super(ChartUpdate, self).form_valid(form)
|
||||
|
||||
|
||||
class ChartDelete(LoginRequiredMixin, DeleteView):
|
||||
model = Chart
|
||||
success_url = '/layers/'
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ChartDelete, self).get_object(queryset=queryset)
|
||||
is_chart_owner = (self.request.user == obj.created_by)
|
||||
lyr_obj = Layer.objects.get(pk=obj.layer)
|
||||
is_lyr_owner = (self.request.user == lyr_obj.owner)
|
||||
if not (self.request.user.is_superuser or is_chart_owner or is_lyr_owner):
|
||||
raise PermissionDenied
|
||||
return obj
|
||||
|
||||
|
||||
class ChartList(ListView):
|
||||
model = Chart
|
||||
fields = '__all__'
|
||||
template_name = 'charts_app/chart_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ChartList, self).get_context_data(**kwargs)
|
||||
return context
|
||||
@@ -53,7 +53,7 @@ LOCALE_PATHS = (
|
||||
os.path.join(LOCAL_ROOT, 'locale'),
|
||||
) + LOCALE_PATHS
|
||||
|
||||
INSTALLED_APPS = INSTALLED_APPS + ('haitidata', 'osgeo_importer', 'geonode-client')
|
||||
INSTALLED_APPS = INSTALLED_APPS + ('haitidata', 'osgeo_importer', 'geonode-client', 'charts_app', 'wfs_harvest')
|
||||
|
||||
LAYER_PREVIEW_LIBRARY = 'react'
|
||||
|
||||
@@ -111,6 +111,9 @@ BROKER_URL = "amqp://guest@localhost:5672"
|
||||
CELERY_ALWAYS_EAGER = True
|
||||
IMPORT_TASK_SOFT_TIME_LIMIT = 90
|
||||
|
||||
MAX_CSV_RECORDS = 20000
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
SOCIAL_ORIGINS = [{
|
||||
"label":"paper-plane-o",
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,39 @@
|
||||
/* Add shadow effect to chart. If you don't like it, get rid of it. */
|
||||
svg {
|
||||
-webkit-filter: drop-shadow( 0px 3px 3px rgba(0,0,0,.3) );
|
||||
filter: drop-shadow( 0px 3px 3px rgba(0,0,0,.25) );
|
||||
}
|
||||
|
||||
/*Styling for the lines connecting the labels to the slices*/
|
||||
polyline{
|
||||
opacity: .3;
|
||||
stroke: black;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
/* Make the percentage on the text labels bold*/
|
||||
.labelName tspan {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* In biology we generally italicise species names. */
|
||||
.labelName {
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Tooltip for line chart */
|
||||
|
||||
div.tooltip {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
height: 28px;
|
||||
padding: 2px;
|
||||
background: lightsteelblue;
|
||||
border: 0px;
|
||||
border-radius: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
function barChart(cat, qnt, title,id){
|
||||
var width = function(){ return document.getElementById(id).offsetWidth},
|
||||
height = 500,
|
||||
padding = 5,
|
||||
xScale = d3.scaleBand(),
|
||||
yScale = d3.scaleLinear(),
|
||||
colour = d3.scaleOrdinal(d3.schemeCategory20),
|
||||
x,
|
||||
y,
|
||||
margin = { top: 30, bottom: 30, left: 30, right: 120 },
|
||||
floatFormat = d3.format("." + d3.precisionFixed(0.5) + "f"),
|
||||
xAxis = d3.axisBottom(xScale),
|
||||
yAxis = d3.axisLeft(yScale);
|
||||
|
||||
function my(selection){
|
||||
|
||||
selection.each(function(data) {
|
||||
|
||||
var svg_name = "chart_svg" + id;
|
||||
var svg = selection.append('svg')
|
||||
.attr("id", svg_name)
|
||||
.attr("width", width())
|
||||
.attr("height", height);
|
||||
|
||||
var g = svg.selectAll("g")
|
||||
.data([1]);
|
||||
g = g.enter().append("g")
|
||||
.merge(g)
|
||||
.attr("class", svg_name)
|
||||
.attr("transform",
|
||||
"translate(" + margin.left + "," + margin.top +")");
|
||||
|
||||
|
||||
var innerWidth = width() - margin.left - margin.right ;
|
||||
var innerHeight = function(){ return height - margin.top - margin.bottom} ;
|
||||
|
||||
|
||||
xScale
|
||||
.domain(data.map(function (d){ return d[x]; }))
|
||||
.range([0, (innerWidth - margin.right)]);
|
||||
|
||||
|
||||
// X axis
|
||||
var xAxis_name = "x-axis_" + id;
|
||||
var xAxis_call = "." + xAxis_name;
|
||||
var xAxisG = g.selectAll(xAxis_call).data([1]);
|
||||
xAxisG.enter().append("g")
|
||||
.attr("class", xAxis_name)
|
||||
.merge(xAxisG)
|
||||
.attr("transform", "translate(0," + innerHeight() +")")
|
||||
.call(xAxis);
|
||||
|
||||
// labels X axis
|
||||
// rotate text if is longer than...
|
||||
var rotate = 0;
|
||||
d3.select(this).select(xAxis_call).selectAll("text").each(function(){
|
||||
if (this.getBBox().width > (xScale.bandwidth() - (padding*2))){
|
||||
rotate = 1;
|
||||
d3.select(xAxis_call).selectAll("text").attr("transform", "rotate(-90)")
|
||||
.attr("y", 0)
|
||||
.attr("x", -10)
|
||||
.attr("dy", ".35em")
|
||||
.style("text-anchor", "end");
|
||||
}});
|
||||
|
||||
// adjust margin and x axis title
|
||||
var maxh = 15;
|
||||
if (rotate == 1) {
|
||||
d3.select(this).select(xAxis_call).selectAll("text").each(function(){
|
||||
if (this.getBBox().width > maxh)
|
||||
maxh = this.getBBox().width;
|
||||
});};
|
||||
margin.bottom = margin.bottom + maxh;
|
||||
d3.select(xAxis_call).attr("transform", "translate(0," + (innerHeight()) + ")");
|
||||
|
||||
var text_name = "text_" + id;
|
||||
g.append("text")
|
||||
.attr("class", text_name)
|
||||
.attr("transform", "translate(" + (innerWidth/2) + ", " + (innerHeight() + margin.bottom - 5) + ")")
|
||||
.style("font-size", "12px")
|
||||
.style("text-anchor", "middle")
|
||||
.text(cat);
|
||||
|
||||
yScale
|
||||
.domain([0, d3.max(data, function (d){ return d[y] ;})])
|
||||
.range([innerHeight(), 0]);
|
||||
|
||||
// Y axis
|
||||
var yAxis_name = "y-axis_" + id;
|
||||
var yAxis_call = "." + yAxis_name;
|
||||
var yAxisG = g.selectAll(yAxis_call).data([1]);
|
||||
yAxisG.enter().append("g")
|
||||
.attr("class", yAxis_name)
|
||||
.merge(yAxisG)
|
||||
.call(yAxis);
|
||||
|
||||
//labels Y axis
|
||||
var maxw = 0;
|
||||
d3.select(this).select(yAxis_call).selectAll("text").each(function(){
|
||||
if (this.getBBox().width > maxw) {
|
||||
maxw = this.getBBox().width;
|
||||
}
|
||||
});
|
||||
margin.left = margin.left + maxw;
|
||||
g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
var yLabel = function(){
|
||||
if (title == "Count"){
|
||||
return "Count";
|
||||
} else {
|
||||
return qnt;
|
||||
}};
|
||||
|
||||
g.append("text")
|
||||
.attr("class", text_name)
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("x", 0 - (innerHeight()/2))
|
||||
.attr("y", 0 - (margin.left))
|
||||
.attr("dy", "1em")
|
||||
.style("font-size", "12px")
|
||||
.style("text-anchor", "middle")
|
||||
.text(yLabel);
|
||||
|
||||
// bars
|
||||
var rects = g.selectAll("rect")
|
||||
.data(data);
|
||||
rects.exit().remove();
|
||||
rects.enter().append("rect")
|
||||
.merge(rects)
|
||||
.attr("x", function (d){ return xScale(d[x]) + padding; })
|
||||
.attr("y", function (d){ return yScale(d[y]); })
|
||||
.attr("fill", function(d) { return colour(xScale(d[x])); })
|
||||
.attr("width", xScale.bandwidth() - (padding*2))
|
||||
.attr("height", function (d){
|
||||
return innerHeight() - yScale(d.value);
|
||||
});
|
||||
|
||||
//chart legend
|
||||
var leg_name = "legend" + id;
|
||||
var leg_call = "." + leg_name;
|
||||
var legend = svg.selectAll("leg_call")
|
||||
.data(data)
|
||||
.enter().append("g")
|
||||
.attr("class", leg_name)
|
||||
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
|
||||
|
||||
legend.append("rect")
|
||||
.attr("x", width() - 18)
|
||||
.attr("width", 18)
|
||||
.attr("height", 18)
|
||||
.style("fill", function(d) { return colour(xScale(d[x]))} );
|
||||
|
||||
legend.append("text")
|
||||
.attr("class", text_name)
|
||||
.attr("x", width() - 24)
|
||||
.attr("y", 9)
|
||||
.attr("dy", ".35em")
|
||||
.style("text-anchor", "end")
|
||||
.text(function (d){ return d[x] + ": " + floatFormat(d[y]); });
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
my.x = function (_){
|
||||
return arguments.length ? (x = _, my) : x;
|
||||
};
|
||||
|
||||
my.y = function (_){
|
||||
return arguments.length ? (y = _, my) : y;
|
||||
};
|
||||
|
||||
my.width = function (_){
|
||||
return arguments.length ? (width = _, my) : width;
|
||||
};
|
||||
|
||||
my.height = function (_){
|
||||
return arguments.length ? (height = _, my) : height;
|
||||
};
|
||||
|
||||
my.padding = function (_){
|
||||
return arguments.length ? (xScale.padding(_), my) : xScale.padding();
|
||||
};
|
||||
|
||||
return my;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
var getCsv = function(csv_link, category, quantity, agg, chartType, id){
|
||||
var data = [];
|
||||
d3.csv(csv_link, function(error, csv) {
|
||||
if (error) throw error;
|
||||
csv.forEach(function(row){
|
||||
var qnty_v; var ctgy_v; var i = 0;
|
||||
while (i < Object.keys(row).length){
|
||||
if (Object.keys(row)[i] == quantity){
|
||||
qnty_v = +Object.values(row)[i]
|
||||
} else if (Object.keys(row)[i] == category){
|
||||
if (Object.values(row)[i].length >= 20){
|
||||
|
||||
ctgy_v = Object.values(row)[i].substring(0, 17) + "...";
|
||||
} else {
|
||||
ctgy_v = Object.values(row)[i];
|
||||
};
|
||||
}
|
||||
i++;
|
||||
};
|
||||
data.push({qnty : qnty_v , ctgy : ctgy_v });
|
||||
})
|
||||
|
||||
|
||||
var dropZeros = function(dato){
|
||||
return dato.filter(function(d){
|
||||
if (d.qnty !== 0){
|
||||
return d.qnty;
|
||||
}})
|
||||
};
|
||||
|
||||
function groupData_sum(dato){
|
||||
return d3.nest().key(function(d) {return d.ctgy;})
|
||||
.rollup(function(v) {return d3.sum(v, function(d) {return d.qnty;});})
|
||||
.entries(dato);
|
||||
};
|
||||
|
||||
function groupData_mean(dato){
|
||||
return d3.nest().key(function(d) {return d.ctgy;})
|
||||
.rollup(function(v) {return d3.mean(v, function(d) {return d.qnty;});})
|
||||
.entries(dato);
|
||||
};
|
||||
|
||||
function groupData_min(dato){
|
||||
return d3.nest().key(function(d) {return d.ctgy;})
|
||||
.rollup(function(v) {return d3.min(v, function(d) {return d.qnty;});})
|
||||
.entries(dato);
|
||||
};
|
||||
|
||||
function groupData_max(dato){
|
||||
return d3.nest().key(function(d) {return d.ctgy;})
|
||||
.rollup(function(v) {return d3.max(v, function(d) {return d.qnty;});})
|
||||
.entries(dato);
|
||||
};
|
||||
|
||||
function groupData_count(dato){
|
||||
return d3.nest().key(function(d) {return d.ctgy;})
|
||||
.rollup(function(v) { return v.length;})
|
||||
.entries(dato);
|
||||
};
|
||||
|
||||
function descendent(a,b){
|
||||
if (a.value < b.value) return 1;
|
||||
if (a.value > b.value) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
var title;
|
||||
var dati;
|
||||
function aggregateData(data, agg){
|
||||
|
||||
if (agg == 0){
|
||||
dati = groupData_sum(dropZeros(data)).sort(descendent);
|
||||
title = "Sum";
|
||||
} else if (agg == 1){
|
||||
dati = groupData_mean(data).sort(descendent);
|
||||
title = "Mean";
|
||||
} else if (agg == 2){
|
||||
dati = groupData_count(data).sort(descendent);
|
||||
title = "Count";
|
||||
} else if (agg == 3){
|
||||
dati = groupData_max(dropZeros(data)).sort(descendent);
|
||||
title = "Maximum";
|
||||
} else if (agg == 4){
|
||||
dati = groupData_min(dropZeros(data)).sort(descendent);;
|
||||
title = "Minimum";
|
||||
};
|
||||
return dati;
|
||||
};
|
||||
|
||||
function create(chartType){
|
||||
if (chartType == 0){
|
||||
aggregateData(data, agg);
|
||||
if (dati.length > 20){
|
||||
var firstSet = dati.slice(0,19);
|
||||
var lastSet = dati.slice(19,).reduce(function(a, b){ return {key: 'others', value: a.value + b.value }});
|
||||
firstSet.push(lastSet);
|
||||
dati = firstSet;
|
||||
};
|
||||
var chartArea = "#" + id;
|
||||
var bar = barChart(category, quantity, title, id)
|
||||
.x('key')
|
||||
.y('value')
|
||||
d3.select(chartArea)
|
||||
.datum(dati)
|
||||
.call(bar);
|
||||
} else if (chartType == 1){
|
||||
aggregateData(data, agg)
|
||||
if (dati.length > 10){
|
||||
var firstSet = dati.slice(0,9);
|
||||
var lastSet = dati.slice(9,).reduce(function(a, b){ return {key: 'others', value: a.value + b.value }});
|
||||
firstSet.push(lastSet);
|
||||
dati = firstSet;
|
||||
};
|
||||
var chartArea = "#" + id;
|
||||
var pie = pieChart(category, quantity, title, id)
|
||||
.variable('value')
|
||||
.category('key')
|
||||
d3.select(chartArea)
|
||||
.datum(dati)
|
||||
.call(pie);
|
||||
} else if (chartType == 2){
|
||||
aggregateData(data, agg)
|
||||
if (dati.length > 10){
|
||||
var firstSet = dati.slice(0,9);
|
||||
var lastSet = dati.slice(9,).reduce(function(a, b){ return {key: 'others', value: a.value + b.value }});
|
||||
firstSet.push(lastSet);
|
||||
dati = firstSet;
|
||||
};
|
||||
var chartArea = "#" + id;
|
||||
var donut = donutChart(category, quantity, title, id)
|
||||
.variable('value')
|
||||
.category('key')
|
||||
d3.select(chartArea)
|
||||
.datum(dati)
|
||||
.call(donut);
|
||||
} else if (chartType == 3){
|
||||
aggregateData(data, agg)
|
||||
if (dati.length > 20){
|
||||
var firstSet = dati.slice(0,19);
|
||||
var lastSet = dati.slice(19,).reduce(function(a, b){ return {key: 'others', value: a.value + b.value }});
|
||||
firstSet.push(lastSet);
|
||||
dati = firstSet;
|
||||
};
|
||||
var chartArea = "#" + id;
|
||||
var line = lineChart(category, quantity, title, id)
|
||||
.x('key')
|
||||
.y('value')
|
||||
d3.select(chartArea)
|
||||
.datum(dati)
|
||||
.call(line);
|
||||
}
|
||||
};
|
||||
|
||||
create(chartType);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
function donutChart(cat, qnt, title, id){
|
||||
var width = function(){ return document.getElementById(id).parentNode.offsetWidth},
|
||||
height = 500,
|
||||
margin = {top: 80, right: 10, bottom: 10, left: 10},
|
||||
colour = d3.scaleOrdinal(d3.schemeCategory20c), // colour scheme
|
||||
variable, // value in data that will dictate proportions on chart
|
||||
category, // compare data by
|
||||
padAngle = 0.015, // effectively dictates the gap between slices
|
||||
transTime, // transition time
|
||||
updateData,
|
||||
floatFormat = d3.format("." + d3.precisionFixed(0.5) + "f"),
|
||||
cornerRadius = 3; // sets how rounded the corners are on each slice
|
||||
|
||||
function chart(selection){
|
||||
selection.each(function() {
|
||||
// generate chart
|
||||
// ===========================================================================================
|
||||
// Set up constructors for making donut. See https://github.com/d3/d3-shape/blob/master/README.md
|
||||
var radius = Math.min(width(), (height - margin.top)) / 2;
|
||||
|
||||
// creates a new pie generator
|
||||
var pie = d3.pie()
|
||||
.value(function(d) { return floatFormat(d[variable]); })
|
||||
.sort(null);
|
||||
|
||||
// contructs and arc generator. This will be used for the donut. The difference between outer and inner
|
||||
// radius will dictate the thickness of the donut
|
||||
var arc = d3.arc()
|
||||
.outerRadius(radius * 0.8)
|
||||
.innerRadius(radius * 0.6)
|
||||
.cornerRadius(cornerRadius)
|
||||
.padAngle(padAngle);
|
||||
|
||||
// this arc is used for aligning the text labels
|
||||
var outerArc = d3.arc()
|
||||
.outerRadius(radius * 0.83)
|
||||
.innerRadius(radius * 0.83);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// append the svg object to the selection
|
||||
// var svg = selection.append('svg')
|
||||
var svg = selection.append('svg')
|
||||
.attr("id", "chart_svg")
|
||||
.attr('width', width() + margin.left + margin.right)
|
||||
.attr('height', height + margin.top + margin.bottom)
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + width() / 2 + ',' + height / 2 + ')');
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// g elements to keep elements within svg modular
|
||||
svg.append('g').attr('class', 'slices');
|
||||
svg.append('g').attr('class', 'labelName');
|
||||
svg.append('g').attr('class', 'lines');
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add and colour the donut slices
|
||||
var path = svg.select('.slices')
|
||||
.selectAll('path')
|
||||
.data(pie)
|
||||
.enter().append('path')
|
||||
.attr('fill', function(d) { return colour(d.data[category]); })
|
||||
.attr('d', arc);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add text labels
|
||||
var occupedLabel = [];
|
||||
var label = svg.select('.labelName').selectAll('text')
|
||||
.data(pie)
|
||||
.enter().append('text')
|
||||
.attr('dy', '.35em')
|
||||
.html(updateLabelText)
|
||||
.attr('transform', labelTransform)
|
||||
.style('text-anchor', function(d) {
|
||||
// if slice centre is on the left, anchor text to start, otherwise anchor to end
|
||||
return (midAngle(d)) < Math.PI ? 'start' : 'end';
|
||||
});
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add lines connecting labels to slice. A polyline creates straight lines connecting several points
|
||||
var occupedPoint = [];
|
||||
var polyline = svg.select('.lines')
|
||||
.selectAll('polyline')
|
||||
.data(pie)
|
||||
.enter().append('polyline')
|
||||
.attr('points', calculatePoints);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add tooltip to mouse events on slices and labels
|
||||
d3.selectAll('.labelName text, .slices path').call(toolTip);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// FUNCTION TO UPDATE CHART
|
||||
updateData = function() {
|
||||
|
||||
var updatePath = d3.select('.slices').selectAll('path');
|
||||
var updateLines = d3.select('.lines').selectAll('polyline');
|
||||
var updateLabels = d3.select('.labelName').selectAll('text');
|
||||
|
||||
var data0 = path.data(), // store the current data before updating to the new
|
||||
data1 = pie(data);
|
||||
|
||||
// update data attached to the slices, labels, and polylines. the key function assigns the data to
|
||||
// the correct element, rather than in order of how the data appears. This means that if a category
|
||||
// already exists in the chart, it will have its data updated rather than removed and re-added.
|
||||
updatePath = updatePath.data(data1, key);
|
||||
updateLines = updateLines.data(data1, key);
|
||||
updateLabels = updateLabels.data(data1, key);
|
||||
|
||||
// adds new slices/lines/labels
|
||||
updatePath.enter().append('path')
|
||||
.each(function(d, i) { this._current = findNeighborArc(i, data0, data1, key) || d; })
|
||||
.attr('fill', function(d) { return colour(d.data[category]); })
|
||||
.attr('d', arc);
|
||||
|
||||
updateLines.enter().append('polyline')
|
||||
.each(function(d, i) { this._current = findNeighborArc(i, data0, data1, key) || d; })
|
||||
.attr('points', calculatePoints);
|
||||
|
||||
updateLabels.enter().append('text')
|
||||
.each(function(d, i) { this._current = findNeighborArc(i, data0, data1, key) || d; })
|
||||
.html(updateLabelText)
|
||||
.attr('transform', labelTransform)
|
||||
.style('text-anchor', function(d) { return (midAngle(d)) < Math.PI ? 'start' : 'end'; });
|
||||
|
||||
// removes slices/labels/lines that are not in the current dataset
|
||||
updatePath.exit()
|
||||
.transition()
|
||||
.duration(transTime)
|
||||
.attrTween("d", arcTween)
|
||||
.remove();
|
||||
|
||||
updateLines.exit()
|
||||
.transition()
|
||||
.duration(transTime)
|
||||
.attrTween("points", pointTween)
|
||||
.remove();
|
||||
|
||||
updateLabels.exit()
|
||||
.remove();
|
||||
|
||||
// animates the transition from old angle to new angle for slices/lines/labels
|
||||
updatePath.transition().duration(transTime)
|
||||
.attrTween('d', arcTween);
|
||||
|
||||
updateLines.transition().duration(transTime)
|
||||
.attrTween('points', pointTween);
|
||||
|
||||
updateLabels.transition().duration(transTime)
|
||||
.attrTween('transform', labelTween)
|
||||
.styleTween('text-anchor', labelStyleTween);
|
||||
|
||||
updateLabels.html(updateLabelText); // update the label text
|
||||
|
||||
// add tooltip to mouse events on slices and labels
|
||||
d3.selectAll('.labelName text, .slices path').call(toolTip);
|
||||
|
||||
};
|
||||
// ===========================================================================================
|
||||
// Functions
|
||||
// calculates the angle for the middle of a slice
|
||||
function midAngle(d) { return d.startAngle + (d.endAngle - d.startAngle) / 2; }
|
||||
|
||||
// function that creates and adds the tool tip to a selected element
|
||||
function toolTip(selection) {
|
||||
|
||||
// add tooltip (svg circle element) when mouse enters label or slice
|
||||
selection.on('mouseenter', function (data) {
|
||||
|
||||
svg.append('text')
|
||||
.attr('class', 'toolCircle')
|
||||
.attr('dy', -15) // hard-coded. can adjust this to adjust text vertical alignment in tooltip
|
||||
.html(toolTipHTML(data)) // add text to the circle.
|
||||
.style('font-size', '.9em')
|
||||
.style('text-anchor', 'middle'); // centres text in tooltip
|
||||
|
||||
svg.append('circle')
|
||||
.attr('class', 'toolCircle')
|
||||
.attr('r', radius * 0.55) // radius of tooltip circle
|
||||
.style('fill', colour(data.data[category])) // colour based on category mouse is over
|
||||
.style('fill-opacity', 0.35);
|
||||
|
||||
});
|
||||
|
||||
// remove the tooltip when mouse leaves the slice/label
|
||||
selection.on('mouseout', function () {
|
||||
d3.selectAll('.toolCircle').remove();
|
||||
});
|
||||
}
|
||||
|
||||
// function to create the HTML string for the tool tip. Loops through each key in data object
|
||||
// and returns the html string key: value
|
||||
function toolTipHTML(data) {
|
||||
|
||||
var tip = '',
|
||||
i = 0;
|
||||
|
||||
for (var key in data.data) {
|
||||
|
||||
// if value is a number, format it as float
|
||||
var value = (!isNaN(parseFloat(data.data[key]))) ? parseFloat(data.data[key]) : data.data[key];
|
||||
qnt = (title == "Count" ? "Count" : qnt);
|
||||
|
||||
// leave off 'dy' attr for first tspan so the 'dy' attr on text element works. The 'dy' attr on
|
||||
// tspan effectively imitates a line break.
|
||||
if (i === 0) tip += '<tspan x="0">' + cat + ': ' + value + '</tspan>';
|
||||
else tip += '<tspan x="0" dy="1.2em">' + qnt + ': ' + value + '</tspan>';
|
||||
i++;
|
||||
}
|
||||
|
||||
return tip;
|
||||
}
|
||||
|
||||
// calculate the points for the polyline to pass through
|
||||
function calculatePoints(d) {
|
||||
// see label transform function for explanations of these three lines.
|
||||
var pos = outerArc.centroid(d);
|
||||
var rev_pos = outerArc.centroid(d);
|
||||
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
|
||||
// verify label conflict: if overlaps move up of 10
|
||||
if (pos[0] < 0 && pos[1] < 0) {
|
||||
var i = 0;
|
||||
if (occupedPoint.length >= 1){
|
||||
for (i = 0; i < occupedPoint.length; i++){
|
||||
if (pos[1] <= (occupedPoint[i] + 10) && (pos[1] >= (occupedPoint[i] - 10))){
|
||||
pos[1] = pos[1] - 20;
|
||||
rev_pos[1] = rev_pos[1] - 20;
|
||||
}
|
||||
}
|
||||
} occupedPoint.push(pos[1]);
|
||||
}
|
||||
return [arc.centroid(d), rev_pos, pos]
|
||||
}
|
||||
|
||||
function labelTransform(d) {
|
||||
// effectively computes the centre of the slice.
|
||||
// see https://github.com/d3/d3-shape/blob/master/README.md#arc_centroid
|
||||
var pos = outerArc.centroid(d);
|
||||
// verify label conflict: if overlaps move up of 10
|
||||
if (pos[0] < 0 && pos[1] < 0) {
|
||||
var i = 0;
|
||||
if (occupedLabel.length >= 1){
|
||||
for (i = 0; i < occupedLabel.length; i++){
|
||||
if (pos[1] <= (occupedLabel[i] + 10) && (pos[1] >= (occupedLabel[i] - 10))){
|
||||
pos[1] = pos[1] - 20;
|
||||
}
|
||||
}
|
||||
} occupedLabel.push(pos[1]);
|
||||
}
|
||||
// changes the point to be on left or right depending on where label is.
|
||||
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
|
||||
return 'translate(' + pos + ')';
|
||||
}
|
||||
|
||||
function updateLabelText(d) {
|
||||
return d.data[category] + ': <tspan>' + floatFormat(d.data[variable]) + '</tspan>';
|
||||
}
|
||||
|
||||
// function that calculates transition path for label and also it's text anchoring
|
||||
function labelStyleTween(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t){
|
||||
var d2 = interpolate(t);
|
||||
return midAngle(d2) < Math.PI ? 'start':'end';
|
||||
};
|
||||
}
|
||||
|
||||
function labelTween(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t){
|
||||
var d2 = interpolate(t),
|
||||
pos = outerArc.centroid(d2); // computes the midpoint [x,y] of the centre line that would be
|
||||
// generated by the given arguments. It is defined as startangle + endangle/2 and innerR + outerR/2
|
||||
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1); // aligns the labels on the sides
|
||||
return 'translate(' + pos + ')';
|
||||
};
|
||||
}
|
||||
|
||||
function pointTween(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t){
|
||||
var d2 = interpolate(t),
|
||||
pos = outerArc.centroid(d2);
|
||||
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
|
||||
return [arc.centroid(d2), outerArc.centroid(d2), pos];
|
||||
};
|
||||
}
|
||||
|
||||
// function to calculate the tween for an arc's transition.
|
||||
// see http://bl.ocks.org/mbostock/5100636 for a thorough explanation.
|
||||
function arcTween(d) {
|
||||
var i = d3.interpolate(this._current, d);
|
||||
this._current = i(0);
|
||||
return function(t) { return arc(i(t)); };
|
||||
}
|
||||
|
||||
function findNeighborArc(i, data0, data1, key) {
|
||||
var d;
|
||||
return (d = findPreceding(i, data0, data1, key)) ? {startAngle: d.endAngle, endAngle: d.endAngle}
|
||||
: (d = findFollowing(i, data0, data1, key)) ? {startAngle: d.startAngle, endAngle: d.startAngle}
|
||||
: null;
|
||||
}
|
||||
// Find the element in data0 that joins the highest preceding element in data1.
|
||||
function findPreceding(i, data0, data1, key) {
|
||||
var m = data0.length;
|
||||
while (--i >= 0) {
|
||||
var k = key(data1[i]);
|
||||
for (var j = 0; j < m; ++j) {
|
||||
if (key(data0[j]) === k) return data0[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function key(d) {
|
||||
return d.data[category];
|
||||
}
|
||||
|
||||
// Find the element in data0 that joins the lowest following element in data1.
|
||||
function findFollowing(i, data0, data1, key) {
|
||||
var n = data1.length, m = data0.length;
|
||||
while (++i < n) {
|
||||
var k = key(data1[i]);
|
||||
for (var j = 0; j < m; ++j) {
|
||||
if (key(data0[j]) === k) return data0[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// getter and setter functions. See Mike Bostocks post "Towards Reusable Charts" for a tutorial on how this works.
|
||||
chart.width = function(value) {
|
||||
if (!arguments.length) return width;
|
||||
width = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(value) {
|
||||
if (!arguments.length) return height;
|
||||
height = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.margin = function(value) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.radius = function(value) {
|
||||
if (!arguments.length) return radius;
|
||||
radius = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.padAngle = function(value) {
|
||||
if (!arguments.length) return padAngle;
|
||||
padAngle = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.cornerRadius = function(value) {
|
||||
if (!arguments.length) return cornerRadius;
|
||||
cornerRadius = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.colour = function(value) {
|
||||
if (!arguments.length) return colour;
|
||||
colour = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.variable = function(value) {
|
||||
if (!arguments.length) return variable;
|
||||
variable = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.category = function(value) {
|
||||
if (!arguments.length) return category;
|
||||
category = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.transTime = function(value) {
|
||||
if (!arguments.length) return transTime;
|
||||
transTime = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.data = function(value) {
|
||||
if (!arguments.length) return data;
|
||||
data = value;
|
||||
if (typeof updateData === 'function') updateData();
|
||||
return chart;
|
||||
};
|
||||
|
||||
return chart;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
var dict_title_tc = {0:"Bar",1:"Pie", 2:"Donut", 3:"Line"};
|
||||
var dict_title_agg = {0:"Sum",1:"Mean",2:"Count", 3:"Max", 4:"Min"};
|
||||
|
||||
function get_chart_detail(){
|
||||
var id = "chart_area_plain"
|
||||
//create area for chart
|
||||
var createChartArea = function() {
|
||||
var chartArea = document.getElementById(id);
|
||||
chartArea.style= "display:table; height:100%";
|
||||
};
|
||||
|
||||
// update chart
|
||||
var replaceChart = function(){
|
||||
doc_chart = document.getElementById(id);
|
||||
if(doc_chart.childElementCount >= 1){
|
||||
doc_chart.removeChild(doc_chart.childNodes[1]);
|
||||
createChartArea();
|
||||
};
|
||||
};
|
||||
|
||||
var chartArea = document.getElementById(id);
|
||||
createChartArea();
|
||||
replaceChart();
|
||||
console.log("son qui")
|
||||
getCsv(csv_url, category, quantity, agg, chart_type, id);
|
||||
}
|
||||
|
||||
function title_detail(){
|
||||
|
||||
chart_type = dict_title_tc[chart_type]
|
||||
return document.getElementById("title_detail").innerHTML = chart_type + " chart based on " + lyr + " layer (Category field: " + category + ", Quantity field: " + quantity + ")";
|
||||
}
|
||||
|
||||
function get_chart_popup(){
|
||||
var id = "chart_area";
|
||||
//create area for chart
|
||||
var createChartArea = function() {
|
||||
var chartArea = document.getElementById(id);
|
||||
chartArea.style= "display:table";
|
||||
};
|
||||
// update chart
|
||||
var replaceChart = function(){
|
||||
doc_chart = document.getElementById(id);
|
||||
if(doc_chart.childElementCount >= 1){
|
||||
doc_chart.removeChild(doc_chart.childNodes[1]);
|
||||
createChartArea();
|
||||
};
|
||||
};
|
||||
if (document.getElementById("id_title").value == ""){
|
||||
document.getElementById("chart_title").innerHTML = suggestedTitle;
|
||||
}else document.getElementById("chart_title").innerHTML = document.getElementById("id_title").value;
|
||||
createChartArea();
|
||||
replaceChart();
|
||||
attachAbstract();
|
||||
getCsv(csv_url, category, quantity, agg, chart_type, id);
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
function lineChart(cat, qnt, title, id){
|
||||
var width = function(){ return document.getElementById(id).offsetWidth},
|
||||
height = 500,
|
||||
xScale = d3.scaleBand(),
|
||||
yScale = d3.scaleLinear(),
|
||||
colour = "steelblue",
|
||||
x,
|
||||
y,
|
||||
margin = { top: 30, bottom: 30, left: 30, right: 30 },
|
||||
floatFormat = d3.format("." + d3.precisionFixed(0.5) + "f"),
|
||||
xAxis = d3.axisBottom(xScale),
|
||||
yAxis = d3.axisLeft(yScale);
|
||||
|
||||
function my(selection){
|
||||
|
||||
selection.each(function(data) {
|
||||
|
||||
var svg_name = "chart_svg" + id;
|
||||
var svg = selection.append('svg')
|
||||
.attr("id", svg_name)
|
||||
.attr("width", width())
|
||||
.attr("height", height);
|
||||
|
||||
var g = svg.selectAll("g")
|
||||
.data([1]);
|
||||
g = g.enter().append("g")
|
||||
.merge(g)
|
||||
.attr("class", svg_name)
|
||||
.attr("transform",
|
||||
"translate(" + margin.left + "," + margin.top +")");
|
||||
|
||||
|
||||
var innerWidth = width() - margin.left - margin.right ;
|
||||
var innerHeight = function(){ return height - margin.top - margin.bottom} ;
|
||||
|
||||
|
||||
xScale
|
||||
.domain(data.map(function (d){ return d[x]; }))
|
||||
.range([0, (innerWidth - margin.right)]);
|
||||
|
||||
|
||||
// X axis
|
||||
var xAxis_name = "x-axis_" + id;
|
||||
var xAxis_call = "." + xAxis_name;
|
||||
var xAxisG = g.selectAll(xAxis_call).data([1]);
|
||||
xAxisG.enter().append("g")
|
||||
.attr("class", xAxis_name)
|
||||
.merge(xAxisG)
|
||||
.attr("transform", "translate(0," + innerHeight() +")")
|
||||
.call(xAxis);
|
||||
|
||||
// labels X ax,is
|
||||
// rotate text if is longer than...
|
||||
var rotate = 0;
|
||||
d3.select(this).select(xAxis_call).selectAll("text").each(function(){
|
||||
if (this.getBBox().width > xScale.bandwidth()){
|
||||
rotate = 1
|
||||
d3.select(xAxis_call).selectAll("text").attr("transform", "rotate(-90)")
|
||||
.attr("y", 0)
|
||||
.attr("x", -10)
|
||||
.attr("dy", ".35em")
|
||||
.style("text-anchor", "end");
|
||||
}});
|
||||
|
||||
// adjust margin and x axis title
|
||||
var maxh = 15;
|
||||
if (rotate == 1) {
|
||||
d3.select(this).select(".x-axis").selectAll("text").each(function(){
|
||||
if (this.getBBox().width > maxh)
|
||||
maxh = this.getBBox().width;
|
||||
});};
|
||||
margin.bottom = margin.bottom + maxh;
|
||||
d3.select(xAxis_call).attr("transform", "translate(0," + (innerHeight()) + ")");
|
||||
|
||||
var text_name = "text_" + id;
|
||||
g.append("text")
|
||||
.attr("class", text_name)
|
||||
.attr("transform", "translate(" + (innerWidth/2) + ", " + (innerHeight() + margin.bottom - 5) + ")")
|
||||
.style("font-size", "12px")
|
||||
.style("text-anchor", "middle")
|
||||
.text(cat);
|
||||
|
||||
yScale
|
||||
.domain([0, d3.max(data, function (d){ return d[y] ;})])
|
||||
.range([innerHeight(), 0]);
|
||||
|
||||
// Y axis
|
||||
var yAxis_name = "y-axis_" + id;
|
||||
var yAxis_call = "." + yAxis_name;
|
||||
var yAxisG = g.selectAll(yAxis_call).data([1]);
|
||||
yAxisG.enter().append("g")
|
||||
.attr("class", yAxis_name)
|
||||
.merge(yAxisG)
|
||||
.call(yAxis);
|
||||
|
||||
//labels Y axis
|
||||
var maxw = 0;
|
||||
d3.select(this).select(yAxis_call).selectAll("text").each(function(){
|
||||
if (this.getBBox().width > maxw) {
|
||||
maxw = this.getBBox().width;
|
||||
}
|
||||
});
|
||||
margin.left = margin.left + maxw;
|
||||
g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
var yLabel = function(){
|
||||
if (title == "Count"){
|
||||
return "Count";
|
||||
} else {
|
||||
return qnt;
|
||||
}};
|
||||
|
||||
g.append("text")
|
||||
.attr("class", text_name)
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("x", 0 - (innerHeight()/2))
|
||||
.attr("y", 0 - (margin.left))
|
||||
.attr("dy", "1em")
|
||||
.style("font-size", "12px")
|
||||
.style("text-anchor", "middle")
|
||||
.text(yLabel);
|
||||
|
||||
//div for tooltip
|
||||
var div = d3.select("body").append("div")
|
||||
.style("width","auto")
|
||||
.style("height", "auto")
|
||||
.attr("class", "tooltip")
|
||||
.style("opacity", 0);
|
||||
|
||||
|
||||
// lines
|
||||
var line = d3.line()
|
||||
.x(function(d) { return xScale(d[x]); })
|
||||
.y(function(d) { return yScale(d[y]); });
|
||||
|
||||
|
||||
g.append("path")
|
||||
.data([data])
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "steelblue")
|
||||
.attr("stroke-linejoin", "round")
|
||||
.attr("stroke-linecap", "round")
|
||||
.attr("stroke-width", 1.5)
|
||||
.attr("transform", "translate(" + (xScale.bandwidth() /2) + ", 0)")
|
||||
.attr("d", line);
|
||||
|
||||
g.selectAll("dot")
|
||||
.data(data)
|
||||
.enter().append("circle")
|
||||
.attr("r", 3.5)
|
||||
.attr("transform", "translate(" + (xScale.bandwidth() /2) + ", 0)")
|
||||
.attr("cx", function(d) { return xScale(d[x]); })
|
||||
.attr("cy", function(d) { return yScale(d[y]); })
|
||||
.on("mouseover", function(d){
|
||||
div.transition()
|
||||
.duration(200)
|
||||
.style("opacity", 1);
|
||||
div.html(cat + ": " + d.key + "<br/>" + yLabel() + ": " + d.value)
|
||||
.style("left", (d3.event.pageX) + "px")
|
||||
.style("top", (d3.event.pageY - 40) + "px");
|
||||
})
|
||||
.on("mouseout", function(d) {
|
||||
div.transition()
|
||||
.duration(500)
|
||||
.style("opacity", 0);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
my.x = function (_){
|
||||
return arguments.length ? (x = _, my) : x;
|
||||
};
|
||||
|
||||
my.y = function (_){
|
||||
return arguments.length ? (y = _, my) : y;
|
||||
};
|
||||
|
||||
my.width = function (_){
|
||||
return arguments.length ? (width = _, my) : width;
|
||||
};
|
||||
|
||||
my.height = function (_){
|
||||
return arguments.length ? (height = _, my) : height;
|
||||
};
|
||||
|
||||
my.padding = function (_){
|
||||
return arguments.length ? (xScale.padding(_), my) : xScale.padding();
|
||||
};
|
||||
|
||||
return my;
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
function pieChart(cat, qnt, title, id){
|
||||
var width = function(){ return document.getElementById(id).parentNode.offsetWidth},
|
||||
height = 500,
|
||||
margin = {top: 80, right: 100, bottom: 10, left: 10},
|
||||
colour = d3.scaleOrdinal(d3.schemeCategory20c), // colour scheme
|
||||
variable, // value in data that will dictate proportions on chart
|
||||
category, // compare data by
|
||||
padAngle = 0.015, // effectively dictates the gap between slices
|
||||
transTime, // transition time
|
||||
updateData,
|
||||
floatFormat = d3.format("." + d3.precisionFixed(0.5) + "f"),
|
||||
cornerRadius = 3; // sets how rounded the corners are on each slice
|
||||
|
||||
function chart(selection){
|
||||
selection.each(function() {
|
||||
// generate chart
|
||||
// ===========================================================================================
|
||||
// Set up constructors for making donut. See https://github.com/d3/d3-shape/blob/master/README.md
|
||||
var radius = Math.min(width(), (height - margin.top)) / 2;
|
||||
|
||||
// creates a new pie generator
|
||||
var pie = d3.pie()
|
||||
.value(function(d) { return floatFormat(d[variable]); })
|
||||
.sort(null);
|
||||
|
||||
// contructs and arc generator. This will be used for the donut. The difference between outer and inner
|
||||
// radius will dictate the thickness of the donut
|
||||
var arc = d3.arc()
|
||||
.outerRadius(radius * 0.8)
|
||||
.innerRadius(2)
|
||||
.cornerRadius(cornerRadius)
|
||||
.padAngle(padAngle);
|
||||
|
||||
// this arc is used for aligning the text labels
|
||||
var outerArc = d3.arc()
|
||||
.outerRadius(radius * 0.83)
|
||||
.innerRadius(radius * 0.83);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// append the svg object to the selection
|
||||
var svg = selection.append('svg')
|
||||
.attr("id", "chart_svg")
|
||||
.attr('width', width() + margin.left + margin.right)
|
||||
.attr('height', height + margin.top + margin.bottom)
|
||||
.append('g')
|
||||
.attr('transform', 'translate(' + width() / 2 + ',' + height / 2 + ')');
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// g elements to keep elements within svg modular
|
||||
svg.append('g').attr('class', 'slices');
|
||||
svg.append('g').attr('class', 'labelName');
|
||||
svg.append('g').attr('class', 'lines');
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add and colour the donut slices
|
||||
var path = svg.select('.slices')
|
||||
.selectAll('path')
|
||||
.data(pie)
|
||||
.enter().append('path')
|
||||
.attr('fill', function(d) { return colour(d.data[category]); })
|
||||
.attr('d', arc);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add text labels
|
||||
var occupedLabel = [];
|
||||
var label = svg.select('.labelName').selectAll('text')
|
||||
.data(pie)
|
||||
.enter().append('text')
|
||||
.attr('dy', '.35em')
|
||||
.html(updateLabelText)
|
||||
.attr('transform', labelTransform)
|
||||
.style('text-anchor', function(d) {
|
||||
// if slice centre is on the left, anchor text to start, otherwise anchor to end
|
||||
return (midAngle(d)) < Math.PI ? 'start' : 'end';
|
||||
});
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add lines connecting labels to slice. A polyline creates straight lines connecting several points
|
||||
var occupedPoint = [];
|
||||
var polyline = svg.select('.lines')
|
||||
.selectAll('polyline')
|
||||
.data(pie)
|
||||
.enter().append('polyline')
|
||||
.attr('points', calculatePoints);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// add tooltip to mouse events on slices and labels
|
||||
d3.selectAll('.labelName text, .slices path').call(toolTip);
|
||||
// ===========================================================================================
|
||||
|
||||
// ===========================================================================================
|
||||
// FUNCTION TO UPDATE CHART
|
||||
updateData = function() {
|
||||
|
||||
var updatePath = d3.select('.slices').selectAll('path');
|
||||
var updateLines = d3.select('.lines').selectAll('polyline');
|
||||
var updateLabels = d3.select('.labelName').selectAll('text');
|
||||
|
||||
var data0 = path.data(), // store the current data before updating to the new
|
||||
data1 = pie(data);
|
||||
|
||||
// update data attached to the slices, labels, and polylines. the key function assigns the data to
|
||||
// the correct element, rather than in order of how the data appears. This means that if a category
|
||||
// already exists in the chart, it will have its data updated rather than removed and re-added.
|
||||
updatePath = updatePath.data(data1, key);
|
||||
updateLines = updateLines.data(data1, key);
|
||||
updateLabels = updateLabels.data(data1, key);
|
||||
|
||||
// adds new slices/lines/labels
|
||||
updatePath.enter().append('path')
|
||||
.each(function(d, i) { this._current = findNeighborArc(i, data0, data1, key) || d; })
|
||||
.attr('fill', function(d) { return colour(d.data[category]); })
|
||||
.attr('d', arc);
|
||||
|
||||
updateLines.enter().append('polyline')
|
||||
.each(function(d, i) { this._current = findNeighborArc(i, data0, data1, key) || d; })
|
||||
.attr('points', calculatePoints);
|
||||
|
||||
updateLabels.enter().append('text')
|
||||
.each(function(d, i) { this._current = findNeighborArc(i, data0, data1, key) || d; })
|
||||
.html(updateLabelText)
|
||||
.attr('transform', labelTransform)
|
||||
.style('text-anchor', function(d) { return (midAngle(d)) < Math.PI ? 'start' : 'end'; });
|
||||
|
||||
// removes slices/labels/lines that are not in the current dataset
|
||||
updatePath.exit()
|
||||
.transition()
|
||||
.duration(transTime)
|
||||
.attrTween("d", arcTween)
|
||||
.remove();
|
||||
|
||||
updateLines.exit()
|
||||
.transition()
|
||||
.duration(transTime)
|
||||
.attrTween("points", pointTween)
|
||||
.remove();
|
||||
|
||||
updateLabels.exit()
|
||||
.remove();
|
||||
|
||||
// animates the transition from old angle to new angle for slices/lines/labels
|
||||
updatePath.transition().duration(transTime)
|
||||
.attrTween('d', arcTween);
|
||||
|
||||
updateLines.transition().duration(transTime)
|
||||
.attrTween('points', pointTween);
|
||||
|
||||
updateLabels.transition().duration(transTime)
|
||||
.attrTween('transform', labelTween)
|
||||
.styleTween('text-anchor', labelStyleTween);
|
||||
|
||||
updateLabels.html(updateLabelText); // update the label text
|
||||
|
||||
// add tooltip to mouse events on slices and labels
|
||||
d3.selectAll('.labelName text, .slices path').call(toolTip);
|
||||
|
||||
};
|
||||
// ===========================================================================================
|
||||
// Functions
|
||||
// calculates the angle for the middle of a slice
|
||||
function midAngle(d) { return d.startAngle + (d.endAngle - d.startAngle) / 2};
|
||||
|
||||
// function that creates and adds the tool tip to a selected element
|
||||
function toolTip(selection) {
|
||||
|
||||
// add tooltip (svg circle element) when mouse enters label or slice
|
||||
selection.on('mouseenter', function (data) {
|
||||
|
||||
svg.append('rect')
|
||||
.attr('transform', "translate(-320, 230)")
|
||||
.attr('class', 'toolRect')
|
||||
.attr("width", 190)
|
||||
.attr("height", 60)
|
||||
.style('fill', colour(data.data[category])) // colour based on category mouse is over
|
||||
.style('fill-opacity', 0.35);
|
||||
|
||||
svg.append('text')
|
||||
.attr('transform', "translate(-310, 255)")
|
||||
.attr('class', 'toolRect')
|
||||
.html(toolTipHTML(data)) // add text to the rect
|
||||
.style('font-size', '.9em'); // centres text in tooltip
|
||||
});
|
||||
|
||||
// remove the tooltip when mouse leaves the slice/label
|
||||
selection.on('mouseout', function () {
|
||||
d3.selectAll('.toolRect').remove();
|
||||
});
|
||||
}
|
||||
|
||||
// function to create the HTML string for the tool tip. Loops through each key in data object
|
||||
// and returns the html string key: value
|
||||
function toolTipHTML(data) {
|
||||
|
||||
var tip = '',
|
||||
i = 0;
|
||||
|
||||
for (var key in data.data) {
|
||||
|
||||
// if value is a number, format it as a float
|
||||
var value = (!isNaN(data.data[key])) ? parseFloat(data.data[key]) : data.data[key];
|
||||
qnt = (title == "Count" ? "Count" : qnt);
|
||||
|
||||
// leave off 'dy' attr for first tspan so the 'dy' attr on text element works. The 'dy' attr on
|
||||
// tspan effectively imitates a line break.
|
||||
if (i === 0) tip += '<tspan x="0">' + cat + ': ' + value + '</tspan>';
|
||||
else tip += '<tspan x="0" dy="1.2em">' + qnt + ': ' + floatFormat(value) + '</tspan>';
|
||||
i++;
|
||||
}
|
||||
|
||||
return tip;
|
||||
}
|
||||
|
||||
// calculate the points for the polyline to pass through
|
||||
function calculatePoints(d) {
|
||||
// see label transform function for explanations of these three lines.
|
||||
var pos = outerArc.centroid(d);
|
||||
var rev_pos = outerArc.centroid(d);
|
||||
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
|
||||
// verify label conflict: if overlaps move up of 10
|
||||
if (pos[0] < 0 && pos[1] < 0) {
|
||||
var i = 0;
|
||||
if (occupedPoint.length >= 1){
|
||||
for (i = 0; i < occupedPoint.length; i++){
|
||||
if (pos[1] <= (occupedPoint[i] + 10) && (pos[1] >= (occupedPoint[i] - 10))){
|
||||
pos[1] = pos[1] - 20;
|
||||
rev_pos[1] = rev_pos[1] - 20;
|
||||
}
|
||||
}
|
||||
} occupedPoint.push(pos[1]);
|
||||
}
|
||||
return [arc.centroid(d), rev_pos, pos]
|
||||
}
|
||||
|
||||
|
||||
function labelTransform(d) {
|
||||
// effectively computes the centre of the slice.
|
||||
// see https://github.com/d3/d3-shape/blob/master/README.md#arc_centroid
|
||||
var pos = outerArc.centroid(d);
|
||||
// verify label conflict: if overlaps move up of 10
|
||||
if (pos[0] < 0 && pos[1] < 0) {
|
||||
var i = 0;
|
||||
if (occupedLabel.length >= 1){
|
||||
for (i = 0; i < occupedLabel.length; i++){
|
||||
if (pos[1] <= (occupedLabel[i] + 10) && (pos[1] >= (occupedLabel[i] - 10))){
|
||||
pos[1] = pos[1] - 20;
|
||||
}
|
||||
}
|
||||
} occupedLabel.push(pos[1]);
|
||||
}
|
||||
// changes the point to be on left or right depending on where label is.
|
||||
pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
|
||||
return 'translate(' + pos + ')';
|
||||
}
|
||||
|
||||
function updateLabelText(d) {
|
||||
return d.data[category] + ': <tspan>' + floatFormat(d.data[variable]) + '</tspan>';
|
||||
}
|
||||
|
||||
// function that calculates transition path for label and also it's text anchoring
|
||||
function labelStyleTween(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t){
|
||||
var d2 = interpolate(t);
|
||||
return midAngle(d2) < Math.PI ? 'start':'end';
|
||||
};
|
||||
}
|
||||
|
||||
function labelTween(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t){
|
||||
var d2 = interpolate(t),
|
||||
pos = outerArc.centroid(d2); // computes the midpoint [x,y] of the centre line that would be
|
||||
// generated by the given arguments. It is defined as startangle + endangle/2 and innerR + outerR/2
|
||||
pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1); // aligns the labels on the sides
|
||||
return 'translate(' + pos + ')';
|
||||
};
|
||||
}
|
||||
|
||||
function pointTween(d) {
|
||||
this._current = this._current || d;
|
||||
var interpolate = d3.interpolate(this._current, d);
|
||||
this._current = interpolate(0);
|
||||
return function(t){
|
||||
var d2 = interpolate(t),
|
||||
pos = outerArc.centroid(d2);
|
||||
pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
|
||||
return [arc.centroid(d2), outerArc.centroid(d2), pos];
|
||||
};
|
||||
}
|
||||
|
||||
// function to calculate the tween for an arc's transition.
|
||||
// see http://bl.ocks.org/mbostock/5100636 for a thorough explanation.
|
||||
function arcTween(d) {
|
||||
var i = d3.interpolate(this._current, d);
|
||||
this._current = i(0);
|
||||
return function(t) { return arc(i(t)); };
|
||||
}
|
||||
|
||||
function findNeighborArc(i, data0, data1, key) {
|
||||
var d;
|
||||
return (d = findPreceding(i, data0, data1, key)) ? {startAngle: d.endAngle, endAngle: d.endAngle}
|
||||
: (d = findFollowing(i, data0, data1, key)) ? {startAngle: d.startAngle, endAngle: d.startAngle}
|
||||
: null;
|
||||
}
|
||||
// Find the element in data0 that joins the highest preceding element in data1.
|
||||
function findPreceding(i, data0, data1, key) {
|
||||
var m = data0.length;
|
||||
while (--i >= 0) {
|
||||
var k = key(data1[i]);
|
||||
for (var j = 0; j < m; ++j) {
|
||||
if (key(data0[j]) === k) return data0[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function key(d) {
|
||||
return d.data[category];
|
||||
}
|
||||
|
||||
// Find the element in data0 that joins the lowest following element in data1.
|
||||
function findFollowing(i, data0, data1, key) {
|
||||
var n = data1.length, m = data0.length;
|
||||
while (++i < n) {
|
||||
var k = key(data1[i]);
|
||||
for (var j = 0; j < m; ++j) {
|
||||
if (key(data0[j]) === k) return data0[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// getter and setter functions. See Mike Bostocks post "Towards Reusable Charts" for a tutorial on how this works.
|
||||
chart.width = function(value) {
|
||||
if (!arguments.length) return width;
|
||||
width = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.height = function(value) {
|
||||
if (!arguments.length) return height;
|
||||
height = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.margin = function(value) {
|
||||
if (!arguments.length) return margin;
|
||||
margin = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.radius = function(value) {
|
||||
if (!arguments.length) return radius;
|
||||
radius = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.padAngle = function(value) {
|
||||
if (!arguments.length) return padAngle;
|
||||
padAngle = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.cornerRadius = function(value) {
|
||||
if (!arguments.length) return cornerRadius;
|
||||
cornerRadius = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.colour = function(value) {
|
||||
if (!arguments.length) return colour;
|
||||
colour = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.variable = function(value) {
|
||||
if (!arguments.length) return variable;
|
||||
variable = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.category = function(value) {
|
||||
if (!arguments.length) return category;
|
||||
category = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.transTime = function(value) {
|
||||
if (!arguments.length) return transTime;
|
||||
transTime = value;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.data = function(value) {
|
||||
if (!arguments.length) return data;
|
||||
data = value;
|
||||
if (typeof updateData === 'function') updateData();
|
||||
return chart;
|
||||
};
|
||||
|
||||
return chart;
|
||||
}
|
||||
@@ -175,7 +175,7 @@ form {
|
||||
|
||||
.navbar-header {
|
||||
i {
|
||||
color: white;
|
||||
color: @brand-primary;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
@@ -712,6 +712,60 @@ footer {
|
||||
.grid-spacer {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
#accordion {
|
||||
.panel {
|
||||
border-radius: 0;
|
||||
border-top: 2px solid @gray-light;
|
||||
line-height: 1em;
|
||||
}
|
||||
.panel-heading {
|
||||
background: none;
|
||||
border: 0;
|
||||
}
|
||||
.panel-body {
|
||||
img {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
small {
|
||||
color: @gray;
|
||||
font-size: 0.825em;
|
||||
}
|
||||
form {
|
||||
font-size: 0.825em;
|
||||
.form-control {
|
||||
background: none;
|
||||
color: @gray-dark;
|
||||
font-size: 0.825em;
|
||||
height: 24px;
|
||||
line-height: 1;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#create-chart {
|
||||
small {
|
||||
color: @gray;
|
||||
font-size: 0.825em;
|
||||
}
|
||||
form {
|
||||
font-size: 0.825em;
|
||||
.form-control {
|
||||
background: none;
|
||||
color: @gray-dark;
|
||||
font-size: 0.825em;
|
||||
height: 24px;
|
||||
line-height: 1;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------ OSGeo Importer
|
||||
------------------------------ */
|
||||
body.importer {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends "site_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block title %} {{ block.super }} {% endblock %}
|
||||
|
||||
{% block body_class %}Charts{% endblock %}
|
||||
|
||||
{% block body_outer %}
|
||||
{% block body %}{% endblock body %}
|
||||
{% block sidebar %}{% endblock sidebar %}
|
||||
{% endblock body_outer %}
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends "charts_app/chart_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Deleting" %} {{ object.title }} — {{ block.super }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">{% trans "Remove Chart" %}</h2>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<p class="lead">
|
||||
{% blocktrans with object.title as object_title %}
|
||||
Are you sure you want to remove <a href="{{ object.get_absolute_url }}">{{ object_title }}</a> chart?
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<form method="POST" "{% url "chart_delete" chart.id %}">
|
||||
{% csrf_token %}
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="{% trans "Yes, I am sure" %}" class="btn btn-danger" />
|
||||
<a href="{{ object.get_absolute_url }}" class="btn btn-default">{% trans "No, don't remove it" %}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,368 @@
|
||||
{% extends "charts_app/chart_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load dialogos_tags %}
|
||||
{% load agon_ratings_tags %}
|
||||
{% load bootstrap_tags %}
|
||||
{% load url from future %}
|
||||
{% load base_tags %}
|
||||
{% load guardian_tags %}
|
||||
|
||||
{% block page-title %}
|
||||
{% trans "Explore Charts" %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-icon" href="{% url 'chart_list' %}">
|
||||
<i class="fa fa-align-justify" title="List"></i>
|
||||
</a>
|
||||
<a class="btn btn-primary" href="{% url 'chart_list' %}">
|
||||
{% trans "Charts" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block subheader %}
|
||||
<section id="subheader">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ object.title }}</h2>
|
||||
<p id="title_detail">
|
||||
<span style="text-transform: capitalize;"></span>
|
||||
</p>
|
||||
{% block social_links %}
|
||||
{% if SOCIAL_BUTTONS %}
|
||||
{% include "social_links.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block body_outer %}
|
||||
<script src="/static/js/csv.js"></script>
|
||||
<script src="/static/js/donut.js"></script>
|
||||
<script src="/static/js/bar.js"></script>
|
||||
<script src="/static/js/pie.js"></script>
|
||||
<script src="/static/js/line.js"></script>
|
||||
<script src="/static/js/getChart.js"></script>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div id="chart_area_plain"></div>
|
||||
<h2>Abstract</h2>
|
||||
<p>{{ object.abstract }}</p>
|
||||
<div class="documents-actions">
|
||||
{% include "_actions.html" %}
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<article id="info" class="tab-pane active">
|
||||
|
||||
</article>
|
||||
|
||||
<article id="comments" class="tab-pane">
|
||||
|
||||
</article>
|
||||
|
||||
<article id="rate" class="tab-pane">
|
||||
<!-- TODO: Move this to a reusable template snippet -->
|
||||
|
||||
<h4>{% trans "Rate this document" %}</h4>
|
||||
|
||||
<div id="user_rating" class="category-layer" data-score="{{user_document_rating}}"></div>
|
||||
|
||||
<h4>{% trans 'Average Rating' %}</h4>
|
||||
|
||||
<div class="overall_rating" style="float:left" data-score="{{ document_rating }}"></div> ({{num_votes}})
|
||||
<!-- TODO: Add display of who gave what rating based -->
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<li class="list-group-item">
|
||||
<a href="/table?" id="link">
|
||||
<button class="btn btn-primary btn-md btn-block">{% trans "Download CSV" %}</button>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Modify this Chart" %}</h4>
|
||||
<p>{% trans "Change type of the chart or type of aggregation." %}</p>
|
||||
<a id="updt_chart">
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="collapse" data-target="#form" onclick="update_params();" >{% trans "Modify this Chart" %}</button>
|
||||
</a>
|
||||
<div id="form" class="collapse">
|
||||
<form name="f" method="post" class="form-horizontal" onchange="update_params();" action="{% url 'chart_update' chart.id %}" >
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<input id="id_layer" name="layer" value="{{ resource.id }}" type="hidden">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_type">Type:</label>
|
||||
<div class="col-md-8">
|
||||
<select class="form-control" id="id_type" name="type">
|
||||
<option value="0">Bar chart</option>
|
||||
<option value="1">Pie chart</option>
|
||||
<option value="2">Donut chart</option>
|
||||
<option value="3">Line chart</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_aggr_type">Aggregation:</label>
|
||||
<div class="col-md-8">
|
||||
<select class="form-control" id="id_aggr_type" name="aggr_type">
|
||||
<option value="0">Sum</option>
|
||||
<option value="1">Mean</option>
|
||||
<option value="2">Category count</option>
|
||||
<option value="3">Max</option>
|
||||
<option value="4">Min</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_title">Title:</label>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" id="id_title" maxlength="128" name="title" type="text">
|
||||
<p style="font-size: small; margin:0">Suggested title:</p>
|
||||
<p id="title" style="font-size: small; margin:0">Testo di prova</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_abstract">Abstract:</label>
|
||||
<div class="col-md-8">
|
||||
<textarea class="form-control" cols="40" id="id_abstract" name="abstract" rows="5"></textarea>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-block" data-toggle="modal" data-target="#chart_popup" onclick="get_chart_popup()">Display chart</button>
|
||||
<button type="submit" class="btn btn-primary btn-block" value="Submit">Update chart in db</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'chart_delete' object.id %}" id="del_chart">
|
||||
<button class="btn btn-primary btn-md btn-block">{% trans "Delete this Chart" %}</button>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<button id='saveButton' class="btn btn-primary btn-md btn-block">Export my D3 visualization to PNG</button>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chart_popup" class="modal fade" role="dialog" >
|
||||
<script src="/static/js/csv.js"></script>
|
||||
<script src="/static/js/donut.js"></script>
|
||||
<script src="/static/js/bar.js"></script>
|
||||
<script src="/static/js/pie.js"></script>
|
||||
<script src="/static/js/line.js"></script>
|
||||
<script src="/static/js/getChart.js"></script>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
<div class="modal-dialog" id="chart" style="width: 55%">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 id="chart_title" class="modal-title"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="chart_area" style="display: table; float: inherit; width: 50%;">
|
||||
</div>
|
||||
<div>
|
||||
<h5>Abstract</h5>
|
||||
<p id="abstract"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var typename = "{{ object.layer.typename }}";
|
||||
var lyr = "{{ object.layer.name }}"
|
||||
var category = "{{ object.category }}";
|
||||
var quantity = "{{ object.quantity }}";
|
||||
var agg = "{{ object.aggr_type }}";
|
||||
var chart_type = "{{ object.type }}";
|
||||
var csv_url;
|
||||
var suggestedTitle = "";
|
||||
var dict_title_agg = {0:"Sum",1:"Mean",2:"Count", 3:"Max", 4:"Min"};
|
||||
|
||||
function update_link(){
|
||||
csv_url = '/table?lyrname=' + typename;
|
||||
csv_url = csv_url + '&category=' + category;
|
||||
csv_url = csv_url + '&quantity=' + quantity;
|
||||
document.getElementById('link').href = csv_url;
|
||||
return;
|
||||
}
|
||||
|
||||
function attachAbstract(){
|
||||
var abstractText = document.getElementById("id_abstract").value;
|
||||
document.getElementById('abstract').innerHTML = abstractText;
|
||||
}
|
||||
|
||||
function chart_title(){
|
||||
if (agg == dict_title_agg[2]){
|
||||
suggestedTitle = dict_title_agg[agg] + " of elements for " + category;
|
||||
} else {
|
||||
var title = dict_title_agg[agg]
|
||||
suggestedTitle = title + " of " + quantity + " for " + category;
|
||||
}
|
||||
document.getElementById("title").innerHTML = suggestedTitle;
|
||||
return suggestedTitle;
|
||||
};
|
||||
|
||||
|
||||
function title_eval(){
|
||||
if (document.getElementById("id_title").textLength == 0){
|
||||
document.getElementById("id_title").value = suggestedTitle;
|
||||
}
|
||||
}
|
||||
|
||||
function update_params(){
|
||||
agg = document.f.aggr_type.value;
|
||||
chart_type = document.f.type.value;
|
||||
chart_title();
|
||||
}
|
||||
|
||||
update_link();
|
||||
get_chart_detail();
|
||||
title_detail();
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<script src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script>
|
||||
<script src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
// Set-up the export button
|
||||
d3.select('#saveButton').on('click', function(){
|
||||
var svg_img = document.getElementById("chart_svg");
|
||||
console.log(svg_img)
|
||||
var svgString = getSVGString(document.getElementById("chart_svg"));
|
||||
svgString2Image( svgString, 300, 250, 'png', save ); // passes Blob and filesize String to the callback
|
||||
|
||||
function save( dataBlob, filesize ){
|
||||
saveAs( dataBlob, 'D3 vis exported to PNG.png' ); // FileSaver.js function
|
||||
}
|
||||
});
|
||||
|
||||
// Below are the functions that handle actual exporting:
|
||||
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
|
||||
function getSVGString( svgNode ) {
|
||||
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
|
||||
var cssStyleText = getCSSStyles( svgNode );
|
||||
appendCSS( cssStyleText, svgNode );
|
||||
|
||||
var serializer = new XMLSerializer();
|
||||
var svgString = serializer.serializeToString(svgNode);
|
||||
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
|
||||
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
|
||||
|
||||
return svgString;
|
||||
|
||||
function getCSSStyles( parentElement ) {
|
||||
var selectorTextArr = [];
|
||||
|
||||
// Add Parent element Id and Classes to the list
|
||||
selectorTextArr.push( '#'+parentElement.id );
|
||||
for (var c = 0; c < parentElement.classList.length; c++)
|
||||
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
|
||||
selectorTextArr.push( '.'+parentElement.classList[c] );
|
||||
|
||||
// Add Children element Ids and Classes to the list
|
||||
var nodes = parentElement.getElementsByTagName("*");
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var id = nodes[i].id;
|
||||
if ( !contains('#'+id, selectorTextArr) )
|
||||
selectorTextArr.push( '#'+id );
|
||||
|
||||
var classes = nodes[i].classList;
|
||||
for (var c = 0; c < classes.length; c++)
|
||||
if ( !contains('.'+classes[c], selectorTextArr) )
|
||||
selectorTextArr.push( '.'+classes[c] );
|
||||
}
|
||||
|
||||
// Extract CSS Rules
|
||||
var extractedCSSText = "";
|
||||
for (var i = 0; i < document.styleSheets.length; i++) {
|
||||
var s = document.styleSheets[i];
|
||||
|
||||
try {
|
||||
if(!s.cssRules) continue;
|
||||
} catch( e ) {
|
||||
if(e.name !== 'SecurityError') throw e; // for Firefox
|
||||
continue;
|
||||
}
|
||||
|
||||
var cssRules = s.cssRules;
|
||||
for (var r = 0; r < cssRules.length; r++) {
|
||||
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
|
||||
extractedCSSText += cssRules[r].cssText;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return extractedCSSText;
|
||||
|
||||
function contains(str,arr) {
|
||||
return arr.indexOf( str ) === -1 ? false : true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function appendCSS( cssText, element ) {
|
||||
var styleElement = document.createElement("style");
|
||||
styleElement.setAttribute("type","text/css");
|
||||
styleElement.innerHTML = cssText;
|
||||
var refNode = element.hasChildNodes() ? element.children[0] : null;
|
||||
element.insertBefore( styleElement, refNode );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function svgString2Image( svgString, width, height, format, callback ) {
|
||||
var format = format ? format : 'png';
|
||||
|
||||
var imgsrc = 'data:image/svg+xml;base64,'+ btoa( unescape( encodeURIComponent( svgString ) ) ); // Convert SVG string to data URL
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
var image = new Image();
|
||||
image.onload = function() {
|
||||
context.clearRect ( 0, 0, width, height );
|
||||
context.drawImage(image, 0, 0, width, height);
|
||||
|
||||
canvas.toBlob( function(blob) {
|
||||
var filesize = Math.round( blob.length/1024 ) + ' KB';
|
||||
if ( callback ) callback( blob, filesize );
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
image.src = imgsrc;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,65 @@
|
||||
{% extends "charts_app/chart_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block title %}{{ block.super }} {% trans "Charts" %}{% endblock %}
|
||||
{% block page-title %}
|
||||
{% trans "Explore Charts" %}
|
||||
{% if user.is_authenticated %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-icon" href="{% url "document_upload" %}">
|
||||
<i class="fa fa-plus" title="Upload Document"></i>
|
||||
</a>
|
||||
<a class="btn btn-primary" href="{% url "document_upload" %}">
|
||||
{% trans "Upload Document" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body_class %}charts
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
{% with include_type_filter='true' %}
|
||||
{% with facet_type='charts' %}
|
||||
{% with header='Chart Type' %}
|
||||
{% with filter='doc_type__in' %}
|
||||
{% include "search/_search_content.html" %}
|
||||
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
<div class="col-md-9">
|
||||
<h1>CHARTS</h1>
|
||||
<ul>
|
||||
{% for chart in object_list %}
|
||||
<li>{{ chart.title }}</li>
|
||||
<li>{{ chart.layer }} - {{ chart.category }} - {{ chart.quantity }}</li>
|
||||
<li>{{ chart.type }} - {{ chart.agg_type }}</li>
|
||||
<li>{{ chart.abstract }}</li>
|
||||
<br>
|
||||
{% empty %}
|
||||
<li>No Charts yet!</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% include "_bulk_permissions_form.html" %}
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% include "_permissions_form_js.html" %}
|
||||
{% endif %}
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
{% with include_spatial='true' %}
|
||||
{% endwith %}
|
||||
{% endblock extra_script %}
|
||||
@@ -0,0 +1,356 @@
|
||||
{% extends "charts_app/chart_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load dialogos_tags %}
|
||||
{% load agon_ratings_tags %}
|
||||
{% load bootstrap_tags %}
|
||||
{% load url from future %}
|
||||
{% load base_tags %}
|
||||
{% load guardian_tags %}
|
||||
|
||||
{% block page-title %}
|
||||
{% trans "Explore Charts" %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-icon" href="{% url 'chart_list' %}">
|
||||
<i class="fa fa-align-justify" title="List"></i>
|
||||
</a>
|
||||
<a class="btn btn-primary" href="{% url 'chart_list' %}">
|
||||
{% trans "Charts" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block subheader %}
|
||||
<section id="subheader">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ object.title }}</h2>
|
||||
<p id="title_detail">
|
||||
<span style="text-transform: capitalize;"></span>
|
||||
</p>
|
||||
{% block social_links %}
|
||||
{% if SOCIAL_BUTTONS %}
|
||||
{% include "social_links.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block body_outer %}
|
||||
<script src="/static/js/csv.js"></script>
|
||||
<script src="/static/js/donut.js"></script>
|
||||
<script src="/static/js/bar.js"></script>
|
||||
<script src="/static/js/pie.js"></script>
|
||||
<script src="/static/js/line.js"></script>
|
||||
<script src="/static/js/getChart.js"></script>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div id="chart_area_plain"></div>
|
||||
<h2>Abstract</h2>
|
||||
<p>{{ object.abstract }}</p>
|
||||
<div class="documents-actions">
|
||||
{% include "_actions.html" %}
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<article id="info" class="tab-pane active">
|
||||
|
||||
</article>
|
||||
|
||||
<article id="comments" class="tab-pane">
|
||||
|
||||
</article>
|
||||
|
||||
<article id="rate" class="tab-pane">
|
||||
<!-- TODO: Move this to a reusable template snippet -->
|
||||
|
||||
<h4>{% trans "Rate this document" %}</h4>
|
||||
|
||||
<div id="user_rating" class="category-layer" data-score="{{user_document_rating}}"></div>
|
||||
|
||||
<h4>{% trans 'Average Rating' %}</h4>
|
||||
|
||||
<div class="overall_rating" style="float:left" data-score="{{ document_rating }}"></div> ({{num_votes}})
|
||||
<!-- TODO: Add display of who gave what rating based -->
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<li class="list-group-item">
|
||||
<a href="/table?" id="link">
|
||||
<button class="btn btn-primary btn-md btn-block">{% trans "Download CSV" %}</button>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Modify this Chart" %}</h4>
|
||||
<p>{% trans "Change type of the chart or type of aggregation." %}</p>
|
||||
<a id="updt_chart">
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="collapse" data-target="#form" onclick="update_params();" >{% trans "Modify this Chart" %}</button>
|
||||
</a>
|
||||
<div id="form" class="collapse">
|
||||
<form name="f" method="post" class="form-horizontal" onchange="update_params();" >
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
<div class="form-group">
|
||||
{{ form.layer.as_hidden }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.category.as_hidden }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.quantity.as_hidden }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.title.errors }}
|
||||
<label class="control-label col-md-2" for="{{ form.title.id_for_label }}">Title:</label>
|
||||
{{ form.title }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.type.errors }}
|
||||
<label class="control-label col-md-2" for="{{ form.type.id_for_label }}">Type:</label>
|
||||
{{ form.type }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.aggr_type.errors }}
|
||||
<label class="control-label col-md-2" for="{{ form.aggr_type.id_for_label }}">Aggregation:</label>
|
||||
{{ form.aggr_type }}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{{ form.abstract.errors }}
|
||||
<label class="control-label col-md-2" for="{{ form.abstract.id_for_label }}">Abstract:</label>
|
||||
{{ form.abstract }}
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-block" data-toggle="modal" data-target="#chart_popup" onclick="get_chart_popup()">Display chart</button>
|
||||
<button type="submit" class="btn btn-primary btn-block" value="Submit">Update chart in db</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<a href="{% url 'chart_delete' object.id %}" id="del_chart">
|
||||
<button class="btn btn-primary btn-md btn-block">{% trans "Delete this Chart" %}</button>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="chart_popup" class="modal fade" role="dialog" >
|
||||
<script src="/static/js/csv.js"></script>
|
||||
<script src="/static/js/donut.js"></script>
|
||||
<script src="/static/js/bar.js"></script>
|
||||
<script src="/static/js/pie.js"></script>
|
||||
<script src="/static/js/line.js"></script>
|
||||
<script src="/static/js/getChart.js"></script>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
<div class="modal-dialog" id="chart" style="width: 55%">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 id="chart_title" class="modal-title"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="chart_area" style="display: table; float: inherit; width: 50%;">
|
||||
</div>
|
||||
<div>
|
||||
<h5>Abstract</h5>
|
||||
<p id="abstract"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
var typename = "{{ object.layer.typename }}";
|
||||
var lyr = "{{ object.layer.name }}"
|
||||
var category = "{{ object.category }}";
|
||||
var quantity = "{{ object.quantity }}";
|
||||
var agg = "{{ object.aggr_type }}";
|
||||
var chart_type = "{{ object.type }}";
|
||||
var csv_url;
|
||||
var suggestedTitle = "";
|
||||
var dict_title_agg = {0:"Sum",1:"Mean",2:"Count", 3:"Max", 4:"Min"};
|
||||
|
||||
function update_link(){
|
||||
csv_url = '/table?lyrname=' + typename;
|
||||
csv_url = csv_url + '&category=' + category;
|
||||
csv_url = csv_url + '&quantity=' + quantity;
|
||||
document.getElementById('link').href = csv_url;
|
||||
return;
|
||||
}
|
||||
|
||||
function attachAbstract(){
|
||||
var abstractText = document.getElementById("id_abstract").value;
|
||||
document.getElementById('abstract').innerHTML = abstractText;
|
||||
}
|
||||
|
||||
function chart_title(){
|
||||
if (agg == dict_title_agg[2]){
|
||||
suggestedTitle = dict_title_agg[agg] + " of elements for " + category;
|
||||
} else {
|
||||
var title = dict_title_agg[agg]
|
||||
suggestedTitle = title + " of " + quantity + " for " + category;
|
||||
}
|
||||
document.getElementById("title").innerHTML = suggestedTitle;
|
||||
return suggestedTitle;
|
||||
};
|
||||
|
||||
|
||||
function title_eval(){
|
||||
if (document.getElementById("id_title").textLength == 0){
|
||||
document.getElementById("id_title").value = suggestedTitle;
|
||||
}
|
||||
}
|
||||
|
||||
function update_params(){
|
||||
agg = document.f.aggr_type.value;
|
||||
chart_type = document.f.type.value;
|
||||
chart_title();
|
||||
}
|
||||
|
||||
update_link();
|
||||
get_chart_detail();
|
||||
title_detail();
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<div>
|
||||
<button id='saveButton'>Export my D3 visualization to PNG</button>
|
||||
</div>
|
||||
<script src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script>
|
||||
<script src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
// Set-up the export button
|
||||
d3.select('#saveButton').on('click', function(){
|
||||
var svg_img = document.getElementById("chart_svg");
|
||||
console.log(svg_img)
|
||||
var svgString = getSVGString(document.getElementById("chart_svg"));
|
||||
svgString2Image( svgString, 300, 250, 'png', save ); // passes Blob and filesize String to the callback
|
||||
|
||||
function save( dataBlob, filesize ){
|
||||
saveAs( dataBlob, 'D3 vis exported to PNG.png' ); // FileSaver.js function
|
||||
}
|
||||
});
|
||||
|
||||
// Below are the functions that handle actual exporting:
|
||||
// getSVGString ( svgNode ) and svgString2Image( svgString, width, height, format, callback )
|
||||
function getSVGString( svgNode ) {
|
||||
svgNode.setAttribute('xlink', 'http://www.w3.org/1999/xlink');
|
||||
var cssStyleText = getCSSStyles( svgNode );
|
||||
appendCSS( cssStyleText, svgNode );
|
||||
|
||||
var serializer = new XMLSerializer();
|
||||
var svgString = serializer.serializeToString(svgNode);
|
||||
svgString = svgString.replace(/(\w+)?:?xlink=/g, 'xmlns:xlink='); // Fix root xlink without namespace
|
||||
svgString = svgString.replace(/NS\d+:href/g, 'xlink:href'); // Safari NS namespace fix
|
||||
|
||||
return svgString;
|
||||
|
||||
function getCSSStyles( parentElement ) {
|
||||
var selectorTextArr = [];
|
||||
|
||||
// Add Parent element Id and Classes to the list
|
||||
selectorTextArr.push( '#'+parentElement.id );
|
||||
for (var c = 0; c < parentElement.classList.length; c++)
|
||||
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
|
||||
selectorTextArr.push( '.'+parentElement.classList[c] );
|
||||
|
||||
// Add Children element Ids and Classes to the list
|
||||
var nodes = parentElement.getElementsByTagName("*");
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var id = nodes[i].id;
|
||||
if ( !contains('#'+id, selectorTextArr) )
|
||||
selectorTextArr.push( '#'+id );
|
||||
|
||||
var classes = nodes[i].classList;
|
||||
for (var c = 0; c < classes.length; c++)
|
||||
if ( !contains('.'+classes[c], selectorTextArr) )
|
||||
selectorTextArr.push( '.'+classes[c] );
|
||||
}
|
||||
|
||||
// Extract CSS Rules
|
||||
var extractedCSSText = "";
|
||||
for (var i = 0; i < document.styleSheets.length; i++) {
|
||||
var s = document.styleSheets[i];
|
||||
|
||||
try {
|
||||
if(!s.cssRules) continue;
|
||||
} catch( e ) {
|
||||
if(e.name !== 'SecurityError') throw e; // for Firefox
|
||||
continue;
|
||||
}
|
||||
|
||||
var cssRules = s.cssRules;
|
||||
for (var r = 0; r < cssRules.length; r++) {
|
||||
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
|
||||
extractedCSSText += cssRules[r].cssText;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return extractedCSSText;
|
||||
|
||||
function contains(str,arr) {
|
||||
return arr.indexOf( str ) === -1 ? false : true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function appendCSS( cssText, element ) {
|
||||
var styleElement = document.createElement("style");
|
||||
styleElement.setAttribute("type","text/css");
|
||||
styleElement.innerHTML = cssText;
|
||||
var refNode = element.hasChildNodes() ? element.children[0] : null;
|
||||
element.insertBefore( styleElement, refNode );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function svgString2Image( svgString, width, height, format, callback ) {
|
||||
var format = format ? format : 'png';
|
||||
|
||||
var imgsrc = 'data:image/svg+xml;base64,'+ btoa( unescape( encodeURIComponent( svgString ) ) ); // Convert SVG string to data URL
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
var context = canvas.getContext("2d");
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
var image = new Image();
|
||||
image.onload = function() {
|
||||
context.clearRect ( 0, 0, width, height );
|
||||
context.drawImage(image, 0, 0, width, height);
|
||||
|
||||
canvas.toBlob( function(blob) {
|
||||
var filesize = Math.round( blob.length/1024 ) + ' KB';
|
||||
if ( callback ) callback( blob, filesize );
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
image.src = imgsrc;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
@@ -20,8 +20,8 @@
|
||||
{% else %}
|
||||
printService: "",
|
||||
{% endif %}
|
||||
/* The URL to a REST map configuration service. This service
|
||||
* provides listing and, with an authenticated user, saving of
|
||||
/* The URL to a REST map configuration service. This service
|
||||
* provides listing and, with an authenticated user, saving of
|
||||
* maps on the server for sharing and editing.
|
||||
*/
|
||||
rest: "{% url "maps_browse" %}",
|
||||
|
||||
@@ -0,0 +1,708 @@
|
||||
{% extends "layers/layer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load dialogos_tags %}
|
||||
{% load agon_ratings_tags %}
|
||||
{% load bootstrap_tags %}
|
||||
{% load pagination_tags %}
|
||||
{% load url from future %}
|
||||
{% load base_tags %}
|
||||
{% load guardian_tags %}
|
||||
|
||||
{% block title %}{{ block.super }} {% trans "Layers" %} | {{ resource.title|default:resource.typename }}{% endblock %}
|
||||
{% block page-title %}
|
||||
{% trans "Explore Layers" %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-icon" href="{% url "layer_browse" %}">
|
||||
<i class="fa fa-bars" title="All Layers"></i>
|
||||
</a>
|
||||
<a class="btn btn-primary" href="{% url "layer_browse" %}">
|
||||
{% trans "All Layers" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
|
||||
{% if TWITTER_CARD %}
|
||||
{% include "base/_resourcebase_twittercard.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if OPENGRAPH_ENABLED %}
|
||||
{% include "base/_resourcebase_opengraph.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if preview == 'geoext' %}
|
||||
{% include "layers/layer_geoext_map.html" %}
|
||||
{% elif preview == 'OL3' %}
|
||||
{% include "layers/layer_ol3_map.html" %}
|
||||
{% elif preview == 'react' %}
|
||||
{% include 'geonode-client/layer_map.html' %}
|
||||
{% else %}
|
||||
{% include "layers/layer_leaflet_map.html" %}
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block body_class %}layers{% endblock %}
|
||||
|
||||
{% block subheader %}
|
||||
<section id="subheader">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ resource.title|default:resource.typename }}</h2>
|
||||
<p>
|
||||
{{ resource.service_type }}
|
||||
{% if resource.category %}
|
||||
{% trans "in" %} <a href="{% url "search" %}?category__identifier__in={{ resource.category.identifier }}">{{ resource.category }}</a> |
|
||||
{% endif %}
|
||||
{% if resource.date %}
|
||||
{% if resource.date_type == 'creation' %} {% trans "Created on" %} {{ resource.date }}
|
||||
{% elif resource.date_type == 'publication' %} {% trans "Published on" %} {{ resource.date }}
|
||||
{% else %} {% trans "Updated on" %} {{ resource.date }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% block social_links %}
|
||||
{% if SOCIAL_BUTTONS %}
|
||||
{% include "social_links.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block body_outer %}
|
||||
<div class="row">
|
||||
|
||||
<!-- Map Here -->
|
||||
|
||||
<div class="col-md-8">
|
||||
<div id="embedded_map" class="mrg-btm">
|
||||
<div id="preview_map"></div>
|
||||
</div>
|
||||
|
||||
<!-- End of Map -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="layer-actions">
|
||||
{% include "_actions.html" %}
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<article id="info" class="tab-pane active">
|
||||
{% include "base/resourcebase_info_panel.html" %}
|
||||
</article>
|
||||
|
||||
{% if resource.is_mosaic %}
|
||||
<article id="granules" class="description tab-pane {% if tab and tab == 'granules' %}active{% endif %}">
|
||||
<div class="paginate-contents">
|
||||
|
||||
<form method="GET" action="{% url "layer_detail" resource.layer.service_typename %}">
|
||||
{% csrf_token %}
|
||||
<input type="text" id="filter" name="filter" style="witdh:400px"/> <input type="submit" value='{% trans "Filter Granules" %}'/>
|
||||
</br>
|
||||
{% if filter %}
|
||||
<strong>{% trans "Active Filter:" %}</strong> <em>{{ filter }}</em>
|
||||
{% endif %}
|
||||
</form>
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- th>{% trans "Granule ID" %}</th -->
|
||||
<th>{% trans "Bounding Box" %} (minX,minY,maxX,maxY)</th>
|
||||
<th>{% trans "Time" %}</th>
|
||||
<!-- th>{% trans "Elevation" %}</th -->
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% autopaginate all_granules.features 10 %}
|
||||
{% for feature in granules.features %}
|
||||
<tr>
|
||||
<!-- td>{{ feature.id }}</td -->
|
||||
<td><script language="Javascript">var bbox = JSON.parse('{{ feature.geometry.coordinates }}'); document.write('{{ granules.crs.properties.name }};' + bbox[0][0][0] + ',' + bbox[0][0][1] + ',' + bbox[0][2][0] + ',' + bbox[0][2][1]);</script></td>
|
||||
<td>{% if feature.properties.time %}{{ feature.properties.time }}{% endif %}</td>
|
||||
<!-- td>{% if feature.properties.elevation %}{{ feature.properties.elevation }}{% endif %}</td -->
|
||||
<td>
|
||||
<a class="btn btn-xs" href="#" onclick="doPreview('{{ resource.service_typename }}', '{{ feature.id }}', '{{ granules.crs.properties.name }}', '{{ feature.geometry.coordinates }}', {% if feature.properties.time %}'{{ feature.properties.time }}'{% else %}null{% endif %});"><img alt="Granule Preview" title="Granule Preview" src="/static/lib/img/map_go.png"/></a>
|
||||
<a class="btn btn-xs" href="{% url "layer_granule_remove" feature.id resource.service_typename %}"><img alt="Granule Remove" title="Granule Remove" src="/static/lib/img/cancel-on.png"/></a>
|
||||
<script language="Javascript">
|
||||
function doPreview(layer_name, feature_id, srs, the_geom, time) {
|
||||
var map = app.mapPanel.map;
|
||||
for (var ll = 0; ll < app.map.layers.length; ll++) {
|
||||
var layer = app.map.layers[ll];
|
||||
if (layer.name == layer_name) {
|
||||
if (time) {
|
||||
var re = new RegExp("(\\+[0-9]{4})", "gm");
|
||||
var timePosition = time;
|
||||
if (re.test(time)) timePosition = time.replace(re, 'Z');
|
||||
map.layers[ll].mergeNewParams({"TIME" : timePosition, "CQL_FILTER" : "IN ('" + feature_id + "')"});
|
||||
}
|
||||
else {
|
||||
map.layers[ll].mergeNewParams({"CQL_FILTER" : "IN ('" + feature_id + "')"});
|
||||
}
|
||||
|
||||
map.layers[ll].addOptions({
|
||||
singleTile: true
|
||||
});
|
||||
|
||||
bbox = JSON.parse(the_geom);
|
||||
bbox = new OpenLayers.Bounds(bbox[0][0][0],bbox[0][0][1],bbox[0][2][0],bbox[0][2][1]).transform(srs, map.projection);
|
||||
|
||||
//map.maxExtent = bbox;
|
||||
//map.restrictedExtent = bbox;
|
||||
map.maxExtent = new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34);
|
||||
map.restrictedExtent = map.maxExtent;
|
||||
|
||||
map.zoomToExtent(bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% paginate %}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
<article id="attributes" class="tab-pane">
|
||||
{% if resource.is_mosaic %}
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Dimension" %}</th>
|
||||
<th>{% trans "Enabled" %}</th>
|
||||
<th>{% trans "Regex" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td title="TIME">{% trans "TIME" %}</td>
|
||||
<td>{% if resource.has_time %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %}</td>
|
||||
<td>{% if resource.time_regex %}{{ resource.time_regex }}{% else %} {% endif %}</td>
|
||||
</tr>
|
||||
<!-- tr>
|
||||
<td title="ELEVATION">{% trans "ELEVATION" %}</td>
|
||||
<td>{% if resource.has_elevation %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %}</td>
|
||||
<td>{% if resource.elevation_regex %}{{ resource.elevation_regex }}{% else %} {% endif %}</td>
|
||||
</tr -->
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if resource.attribute_set.all %}
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Attribute Name" %}</th>
|
||||
<th>{% trans "Label" %}</th>
|
||||
<th>{% trans "Description" %}</th>
|
||||
{% if wps_enabled %}
|
||||
<th>{% trans "Range" %}</th>
|
||||
<th>{% trans "Average" %}</th>
|
||||
<th>{% trans "Median" %}</th>
|
||||
<th>{% trans "Standard Deviation" %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for attribute in resource.attributes %}
|
||||
<tr>
|
||||
<td {% if attribute.attribute_label and attribute.attribute_label != attribute.attribute %}title="{{ attribute.attribute }}"{% endif %}>{{ attribute }}</td>
|
||||
<td>{{ attribute.attribute_label|default:"" }}</td>
|
||||
<td>{{ attribute.description|default:"" }}</td>
|
||||
{% if wps_enabled %}
|
||||
{% if attribute.unique_values == "NA" %}
|
||||
<td>{{ attribute.unique_values }}</td>
|
||||
{% else %}
|
||||
<td>
|
||||
<select name="unique_values">
|
||||
{% for value in attribute.unique_values_as_list %}
|
||||
<option value="{{ value }}">{{ value }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>{{ attribute.average|floatformat:"2" }}</td>
|
||||
<td>{{ attribute.median|floatformat:"2" }}</td>
|
||||
<td>{{ attribute.stddev|floatformat:"2" }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article id="comments" class="tab-pane">
|
||||
{% with resource as obj %}
|
||||
{% include "_comments.html" %}
|
||||
{% endwith %}
|
||||
</article>
|
||||
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
{% with "_geogig_layer_detail.html" as geogig_template %}
|
||||
<article id="geogig" class="tab-pane">
|
||||
{% with resource as obj %}
|
||||
{% include geogig_template %}
|
||||
{% endwith %}
|
||||
</article>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
<article id="rate" class="tab-pane">
|
||||
<!-- TODO: Move this to a reusable template snippet -->
|
||||
{% if request.user.is_authenticated %}
|
||||
<h4>{% trans "Rate this layer" %}</h4>
|
||||
{% user_rating request.user resource "layer" as user_layer_rating %}
|
||||
<div id="user_rating" class="category-layer" data-score="{{ user_layer_rating }}"></div>
|
||||
{% endif %}
|
||||
<h4>{% trans 'Average Rating' %}</h4>
|
||||
{% overall_rating resource "layer" as layer_rating %}
|
||||
{% num_ratings resource as num_votes %}
|
||||
<div class="overall_rating" data-score="{{ layer_rating }}"></div> ({{num_votes}})
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="headingOne">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
|
||||
A Layer Graph
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
|
||||
<div class="panel-body">
|
||||
<img class="img-responsive" src="https://raw.githubusercontent.com/d3/d3-hexbin/master/img/color.jpg">
|
||||
<small>Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="headingTwo">
|
||||
<h4 class="panel-title">
|
||||
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
Another Layer Graph
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseTwo" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
|
||||
<div class="panel-body">
|
||||
<small>Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3 wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus labore sustainable VHS.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="headingThree">
|
||||
<h4 class="panel-title">
|
||||
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
<i class="fa fa-plus"></i> Create graph
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapseThree" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
|
||||
<div class="panel-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1">Category</label>
|
||||
<select class="form-control">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Quantity</label>
|
||||
<select class="form-control">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Type</label>
|
||||
<select class="form-control">
|
||||
<option>Pie Chart</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Aggregation</label>
|
||||
<select class="form-control">
|
||||
<option>1</option>
|
||||
<option>2</option>
|
||||
<option>3</option>
|
||||
<option>4</option>
|
||||
<option>5</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">Create Graph</button>
|
||||
<button type="submit" class="btn btn-default">Save Chart</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="list-group">
|
||||
{% if resource.storeType != "remoteStore" %}
|
||||
<li class="list-group-item">
|
||||
{% if links or links_download %}
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#download-layer">{% trans "Download Layer" %}</button>
|
||||
{% else %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<button class="btn btn-primary btn-md btn-block" id="request-download">{% trans "Request Download" %}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
<div class="modal fade" id="download-layer" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Download Layer" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
{% if links %}
|
||||
<li class="active"><a href="#download_tab1" data-toggle="tab">Images</a></li>
|
||||
{% endif %}
|
||||
{% if links_download %}
|
||||
<li><a href="#download_tab2" data-toggle="tab">Data</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
{% if links %}
|
||||
<div class="tab-pane active" id="download_tab1">
|
||||
<li class="active">
|
||||
<ul>
|
||||
{% for link in links %}
|
||||
<li><a href="{{ link.url }}" target="_blank">{% trans link.name %}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if links_download %}
|
||||
<div class="tab-pane" id="download_tab2">
|
||||
<li>
|
||||
<ul>
|
||||
{% for link in links_download %}
|
||||
<li><a href="{{ link.url }}" target="_blank">{% trans link.name %}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if GEOSERVER_BASE_URL %}
|
||||
{% get_obj_perms request.user for resource.layer as "layer_perms" %}
|
||||
{% endif %}
|
||||
|
||||
<li class="list-group-item">
|
||||
<a href="{% url "layer_metadata_detail" resource.typename %}">
|
||||
<button class="btn btn-primary btn-md btn-block">{% trans "Metadata Detail" %}</button>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if "change_resourcebase_metadata" in perms_list or "change_resourcebase" in perms_list or "delete_resourcebase" in perms_list or "change_layer_style" in layer_perms %}
|
||||
<li class="list-group-item">
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#edit-layer">{% trans "Edit Layer" %}</button>
|
||||
</li>
|
||||
<div class="modal fade" id="edit-layer" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Edit Layer" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row edit-modal">
|
||||
{% if "change_resourcebase_metadata" in perms_list %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-list-alt fa-3x"></i>
|
||||
<h4>{% trans "Metadata" %}</h4>
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_metadata" resource.service_typename %}">{% trans "Edit" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if GEOSERVER_BASE_URL and not resource.service %}
|
||||
{% if "change_layer_style" in layer_perms %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-tint fa-3x"></i>
|
||||
<h4>{% trans "Styles" %}</h4>
|
||||
{% if preview == 'geoext' %}
|
||||
<a class="btn btn-default btn-block btn-xs style-edit" data-dismiss="modal" href="#">{% trans "Edit" %}</a>
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_style_manage" resource.service_typename %}">{% trans "Manage" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if "change_resourcebase" in perms_list %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-photo fa-3x"></i>
|
||||
<h4>{% trans "Thumbnail" %}</h4>
|
||||
<a class="btn btn-default btn-block btn-xs" href="#" id="set_thumbnail">{% trans "Set" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if "change_resourcebase" in perms_list or "delete_resourcebase" in perms_list or "change_layer_data" in layer_perms %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-square-o fa-3x rotate-45"></i>
|
||||
<h4>{% trans "Layer" %}</h4>
|
||||
{% if "change_resourcebase" in perms_list and not resource.service %}
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_replace" resource.service_typename %}">{% trans "Replace" %}</a>
|
||||
{% endif %}
|
||||
{% if resource.storeType == 'dataStore' and "change_layer_data" in layer_perms %}
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "new_map" %}?layer={{resource.service_typename}}">{% trans "Edit data" %}</a>
|
||||
{% endif %}
|
||||
{% if "delete_resourcebase" in perms_list %}
|
||||
<a class="btn btn-danger btn-block btn-xs" href="{% url "layer_remove" resource.service_typename %}">{% trans "Remove" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<li class="list-group-item">
|
||||
<button class="btn btn-default btn-md btn-block" data-toggle="modal" data-target="#download-metadata">{% trans "Download Metadata" %}</button>
|
||||
</li>
|
||||
|
||||
<div class="modal fade" id="download-metadata" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Download Metadata" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="margin-bottom:20px">
|
||||
<h5 class="modal-title" id="myModalLabel">
|
||||
Full metadata
|
||||
</h5>
|
||||
<ul>
|
||||
<li><a target="_blank" href="../catalogue/csw_to_extra_format/{{resource.uuid}}/{{resource.title}}.txt"> Text format </a></li>
|
||||
<li><a target="_blank" href="../catalogue/csw_to_extra_format/{{resource.uuid}}/{{resource.title}}.html"> HTML format </a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="margin-bottom:20px">
|
||||
<h5 class="modal-title" id="myModalLabel">
|
||||
Standard Metadata - XML format
|
||||
</h5>
|
||||
<ul>
|
||||
{% for link in metadata %}
|
||||
<li><a href="{{ link.url }}" target="_blank">{{ link.name }}</a></li>
|
||||
{% endfor %}
|
||||
<ul>
|
||||
</div>
|
||||
{% if resource.storeType == 'dataStore' %}
|
||||
<h5 class="modal-title" id="myModalLabel">Attribute Information</h5>
|
||||
<ul>
|
||||
<li><a href="{{ resource.get_absolute_url }}/feature_catalogue" target="_blank">ISO Feature Catalogue</a></li>
|
||||
<ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if resource.get_legend %}
|
||||
<li class="list-group-item">
|
||||
<h4 class="list-group-item-heading">{%trans "Legend" %}</h4>
|
||||
<p>{{ style.sld_title }}</p>
|
||||
{% if resource.get_legend.link_type == 'image' %}
|
||||
{% if request.user.is_authenticated and 'access_token' in request.session %}
|
||||
<p><img id="legend_icon" src="{{ resource.get_legend.url }}&access_token={{ request.session.access_token }}"></p>
|
||||
{% else %}
|
||||
<p><img id="legend_icon" src="{{ resource.get_legend.url }}"></p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div id='legend_placeholder'></div>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Maps using this layer" %}</h4>
|
||||
{% if resource.maps %}
|
||||
<p>{% trans "List of maps using this layer:" %}</p>
|
||||
{% endif %}
|
||||
<ul class="list-unstyled">
|
||||
{% for maplayer in resource.maps %}
|
||||
<li><a href="{{ maplayer.map.get_absolute_url }}">{{ maplayer.map.title }}</a></li>
|
||||
{% empty %}
|
||||
<li>{% trans "This layer is not currently used in any maps." %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Create a map using this layer" %}</h4>
|
||||
<p>{% trans "Click the button below to generate a new map based on this layer." %}</p>
|
||||
<a href="{% url "new_map" %}?layer={{resource.service_typename}}" class="btn btn-primary btn-block">{% trans "Create a Map" %}</a>
|
||||
</li>
|
||||
|
||||
{% if documents.count > 0 %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Documents related to this layer" %}</h4>
|
||||
<p>{% trans "List of documents related to this layer:" %}</p>
|
||||
<ul class="list-unstyled">
|
||||
{% for document in documents %}
|
||||
<li><a href="{{ document.get_absolute_url }}">{{ document.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Styles" %}</h4>
|
||||
<p>{% trans "The following styles are associated with this layer. Choose a style to view it in the preview map." %}</p>
|
||||
<ul class="list-unstyled">
|
||||
{% for style in resource.styles.all %}
|
||||
<li>
|
||||
{% if resource.default_style == style %}
|
||||
<input type="radio" checked name="style" id="{{ style.name }}" value="{{ style.title }}"/>
|
||||
(default style)
|
||||
{% else %}
|
||||
<input type="radio" name="style" id="{{ style.name }}" value="{{ style.title }}"/>
|
||||
{% endif %}
|
||||
<a href="{{ GEOSERVER_BASE_URL }}{{ style.absolute_url }}" >{{ style.sld_title }}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>{% trans "No styles associated with this layer" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% if "change_resourcebase_permissions" in perms_list %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Permissions" %}</h4>
|
||||
<p>{% trans "Click the button below to change the permissions of this layer." %}</p>
|
||||
<p><a href="#modal_perms" data-toggle="modal" class="btn btn-primary btn-block" data-target="#_permissions">{% trans "Change Layer Permissions" %}</a></p>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if resource.storeType == "remoteStore" %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "External service layer" %}</h4>
|
||||
<div>{% trans "Source" %}: <a href="/services/{{resource.service.id}}">{{ resource.service.title }}</a></div>
|
||||
<div>{% trans "Type" %}: {{ resource.service.type }}</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% include "base/_resourcebase_contact_snippet.html" %}
|
||||
</ul>
|
||||
|
||||
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% include "_permissions_form.html" %}
|
||||
{% endif %}
|
||||
|
||||
</div> <!-- col-md-4 -->
|
||||
</div> <!-- row -->
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
{% with "_geogig_scripts.html" as geogig_scripts_template %}
|
||||
{% include geogig_scripts_template %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% user_rating_js request.user resource "layer" %}
|
||||
{% else %}
|
||||
{% overall_rating resource 'layer' as the_layer_rating %}
|
||||
{% endif %}
|
||||
{% include 'rating.html' %}
|
||||
{% include 'request_download.html' %}
|
||||
<script type="text/javascript">
|
||||
$("#comment_submit_btn").click(function(event) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $("#form_post_comment").attr('action'),
|
||||
data: $("#form_post_comment").serialize(),
|
||||
success: function() {
|
||||
$('#form_post_comment_div').modal().find('form')[0].reset();
|
||||
$('#form_post_comment_div').modal('hide');
|
||||
$('#comments_section').load(window.location.pathname + ' #comments_section',
|
||||
function(){$(this).children().unwrap()})
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
{% if resource.get_legend.link_type == 'json' %}
|
||||
$.getJSON('{{resource.get_legend_url}}', function (data) {
|
||||
var legend = data.layers[0].legend;
|
||||
var items = [];
|
||||
$.each(legend, function (key, dvalue) {
|
||||
items.push('<li><img src="data:image/png;base64,' + dvalue.imageData + '">'+dvalue.label+'</li>');
|
||||
});
|
||||
|
||||
$('<ul/>', {
|
||||
'class': 'arcgis-legend',
|
||||
html: items.join('')
|
||||
|
||||
}).appendTo('#legend_placeholder');
|
||||
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
$('#set_thumbnail').click(function(){
|
||||
createMapThumbnail();
|
||||
$('#edit-layer').modal('toggle');
|
||||
});
|
||||
</script>
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% include "_permissions_form_js.html" %}
|
||||
{% endif %}
|
||||
{% endblock extra_script %}
|
||||
@@ -0,0 +1,601 @@
|
||||
{% extends "layers/layer_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load dialogos_tags %}
|
||||
{% load agon_ratings_tags %}
|
||||
{% load bootstrap_tags %}
|
||||
{% load pagination_tags %}
|
||||
{% load url from future %}
|
||||
{% load base_tags %}
|
||||
{% load guardian_tags %}
|
||||
|
||||
{% block title %}{{ block.super }} {% trans "Layers" %} | {{ resource.title|default:resource.typename }}{% endblock %}
|
||||
{% block page-title %}{% trans "Explore Layers" %}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
|
||||
{% if TWITTER_CARD %}
|
||||
{% include "base/_resourcebase_twittercard.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if OPENGRAPH_ENABLED %}
|
||||
{% include "base/_resourcebase_opengraph.html" %}
|
||||
{% endif %}
|
||||
|
||||
{% if preview == 'geoext' %}
|
||||
{% include "layers/layer_geoext_map.html" %}
|
||||
{% elif preview == 'OL3' %}
|
||||
{% include "layers/layer_ol3_map.html" %}
|
||||
{% elif preview == 'react' %}
|
||||
{% include 'geonode-client/layer_map.html' %}
|
||||
{% else %}
|
||||
{% include "layers/layer_leaflet_map.html" %}
|
||||
{% endif %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block body_class %}layers{% endblock %}
|
||||
|
||||
{% block subheader %}
|
||||
<section id="subheader">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ resource.title|default:resource.typename }}</h2>
|
||||
<p>
|
||||
{{ resource.service_type }}
|
||||
{% if resource.category %}
|
||||
{% trans "in" %} <a href="{% url "search" %}?category__identifier__in={{ resource.category.identifier }}">{{ resource.category }}</a> |
|
||||
{% endif %}
|
||||
{% if resource.date %}
|
||||
{% if resource.date_type == 'creation' %} {% trans "Created on" %} {{ resource.date }}
|
||||
{% elif resource.date_type == 'publication' %} {% trans "Published on" %} {{ resource.date }}
|
||||
{% else %} {% trans "Updated on" %} {{ resource.date }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% block social_links %}
|
||||
{% if SOCIAL_BUTTONS %}
|
||||
{% include "social_links.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block body_outer %}
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
|
||||
<div id="embedded_map" class="mrg-btm">
|
||||
<div id="preview_map"></div>
|
||||
</div>
|
||||
|
||||
<div class="layer-actions">
|
||||
{% include "_actions.html" %}
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<article id="info" class="tab-pane active">
|
||||
{% include "base/resourcebase_info_panel.html" %}
|
||||
</article>
|
||||
|
||||
{% if resource.is_mosaic %}
|
||||
<article id="granules" class="description tab-pane {% if tab and tab == 'granules' %}active{% endif %}">
|
||||
<div class="paginate-contents">
|
||||
|
||||
<form method="GET" action="{% url "layer_detail" resource.layer.service_typename %}">
|
||||
{% csrf_token %}
|
||||
<input type="text" id="filter" name="filter" style="witdh:400px"/> <input type="submit" value='{% trans "Filter Granules" %}'/>
|
||||
</br>
|
||||
{% if filter %}
|
||||
<strong>{% trans "Active Filter:" %}</strong> <em>{{ filter }}</em>
|
||||
{% endif %}
|
||||
</form>
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- th>{% trans "Granule ID" %}</th -->
|
||||
<th>{% trans "Bounding Box" %} (minX,minY,maxX,maxY)</th>
|
||||
<th>{% trans "Time" %}</th>
|
||||
<!-- th>{% trans "Elevation" %}</th -->
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% autopaginate all_granules.features 10 %}
|
||||
{% for feature in granules.features %}
|
||||
<tr>
|
||||
<!-- td>{{ feature.id }}</td -->
|
||||
<td><script language="Javascript">var bbox = JSON.parse('{{ feature.geometry.coordinates }}'); document.write('{{ granules.crs.properties.name }};' + bbox[0][0][0] + ',' + bbox[0][0][1] + ',' + bbox[0][2][0] + ',' + bbox[0][2][1]);</script></td>
|
||||
<td>{% if feature.properties.time %}{{ feature.properties.time }}{% endif %}</td>
|
||||
<!-- td>{% if feature.properties.elevation %}{{ feature.properties.elevation }}{% endif %}</td -->
|
||||
<td>
|
||||
<a class="btn btn-xs" href="#" onclick="doPreview('{{ resource.service_typename }}', '{{ feature.id }}', '{{ granules.crs.properties.name }}', '{{ feature.geometry.coordinates }}', {% if feature.properties.time %}'{{ feature.properties.time }}'{% else %}null{% endif %});"><img alt="Granule Preview" title="Granule Preview" src="/static/lib/img/map_go.png"/></a>
|
||||
<a class="btn btn-xs" href="{% url "layer_granule_remove" feature.id resource.service_typename %}"><img alt="Granule Remove" title="Granule Remove" src="/static/lib/img/cancel-on.png"/></a>
|
||||
<script language="Javascript">
|
||||
function doPreview(layer_name, feature_id, srs, the_geom, time) {
|
||||
var map = app.mapPanel.map;
|
||||
for (var ll = 0; ll < app.map.layers.length; ll++) {
|
||||
var layer = app.map.layers[ll];
|
||||
if (layer.name == layer_name) {
|
||||
if (time) {
|
||||
var re = new RegExp("(\\+[0-9]{4})", "gm");
|
||||
var timePosition = time;
|
||||
if (re.test(time)) timePosition = time.replace(re, 'Z');
|
||||
map.layers[ll].mergeNewParams({"TIME" : timePosition, "CQL_FILTER" : "IN ('" + feature_id + "')"});
|
||||
}
|
||||
else {
|
||||
map.layers[ll].mergeNewParams({"CQL_FILTER" : "IN ('" + feature_id + "')"});
|
||||
}
|
||||
|
||||
map.layers[ll].addOptions({
|
||||
singleTile: true
|
||||
});
|
||||
|
||||
bbox = JSON.parse(the_geom);
|
||||
bbox = new OpenLayers.Bounds(bbox[0][0][0],bbox[0][0][1],bbox[0][2][0],bbox[0][2][1]).transform(srs, map.projection);
|
||||
|
||||
//map.maxExtent = bbox;
|
||||
//map.restrictedExtent = bbox;
|
||||
map.maxExtent = new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34);
|
||||
map.restrictedExtent = map.maxExtent;
|
||||
|
||||
map.zoomToExtent(bbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% paginate %}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
<article id="attributes" class="tab-pane">
|
||||
{% if resource.is_mosaic %}
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Dimension" %}</th>
|
||||
<th>{% trans "Enabled" %}</th>
|
||||
<th>{% trans "Regex" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td title="TIME">{% trans "TIME" %}</td>
|
||||
<td>{% if resource.has_time %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %}</td>
|
||||
<td>{% if resource.time_regex %}{{ resource.time_regex }}{% else %} {% endif %}</td>
|
||||
</tr>
|
||||
<!-- tr>
|
||||
<td title="ELEVATION">{% trans "ELEVATION" %}</td>
|
||||
<td>{% if resource.has_elevation %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %}</td>
|
||||
<td>{% if resource.elevation_regex %}{{ resource.elevation_regex }}{% else %} {% endif %}</td>
|
||||
</tr -->
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if resource.attribute_set.all %}
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Attribute Name" %}</th>
|
||||
<th>{% trans "Label" %}</th>
|
||||
<th>{% trans "Description" %}</th>
|
||||
{% if wps_enabled %}
|
||||
<th>{% trans "Range" %}</th>
|
||||
<th>{% trans "Average" %}</th>
|
||||
<th>{% trans "Median" %}</th>
|
||||
<th>{% trans "Standard Deviation" %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for attribute in resource.attributes %}
|
||||
<tr>
|
||||
<td {% if attribute.attribute_label and attribute.attribute_label != attribute.attribute %}title="{{ attribute.attribute }}"{% endif %}>{{ attribute }}</td>
|
||||
<td>{{ attribute.attribute_label|default:"" }}</td>
|
||||
<td>{{ attribute.description|default:"" }}</td>
|
||||
{% if wps_enabled %}
|
||||
{% if attribute.unique_values == "NA" %}
|
||||
<td>{{ attribute.unique_values }}</td>
|
||||
{% else %}
|
||||
<td>
|
||||
<select name="unique_values">
|
||||
{% for value in attribute.unique_values_as_list %}
|
||||
<option value="{{ value }}">{{ value }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>{{ attribute.average|floatformat:"2" }}</td>
|
||||
<td>{{ attribute.median|floatformat:"2" }}</td>
|
||||
<td>{{ attribute.stddev|floatformat:"2" }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article id="comments" class="tab-pane">
|
||||
{% with resource as obj %}
|
||||
{% include "_comments.html" %}
|
||||
{% endwith %}
|
||||
</article>
|
||||
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
{% with "_geogig_layer_detail.html" as geogig_template %}
|
||||
<article id="geogig" class="tab-pane">
|
||||
{% with resource as obj %}
|
||||
{% include geogig_template %}
|
||||
{% endwith %}
|
||||
</article>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
<article id="rate" class="tab-pane">
|
||||
<!-- TODO: Move this to a reusable template snippet -->
|
||||
{% if request.user.is_authenticated %}
|
||||
<h4>{% trans "Rate this layer" %}</h4>
|
||||
{% user_rating request.user resource "layer" as user_layer_rating %}
|
||||
<div id="user_rating" class="category-layer" data-score="{{ user_layer_rating }}"></div>
|
||||
{% endif %}
|
||||
<h4>{% trans 'Average Rating' %}</h4>
|
||||
{% overall_rating resource "layer" as layer_rating %}
|
||||
{% num_ratings resource as num_votes %}
|
||||
<div class="overall_rating" data-score="{{ layer_rating }}"></div> ({{num_votes}})
|
||||
</article>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
|
||||
<ul class="list-group">
|
||||
{% if resource.storeType != "remoteStore" %}
|
||||
<li class="list-group-item">
|
||||
{% if links or links_download %}
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#download-layer">{% trans "Download Layer" %}</button>
|
||||
{% else %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<button class="btn btn-primary btn-md btn-block" id="request-download">{% trans "Request Download" %}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
<div class="modal fade" id="download-layer" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Download Layer" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
{% if links %}
|
||||
<li class="active"><a href="#download_tab1" data-toggle="tab">Images</a></li>
|
||||
{% endif %}
|
||||
{% if links_download %}
|
||||
<li><a href="#download_tab2" data-toggle="tab">Data</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
{% if links %}
|
||||
<div class="tab-pane active" id="download_tab1">
|
||||
<li class="active">
|
||||
<ul>
|
||||
{% for link in links %}
|
||||
<li><a href="{{ link.url }}" target="_blank">{% trans link.name %}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if links_download %}
|
||||
<div class="tab-pane" id="download_tab2">
|
||||
<li>
|
||||
<ul>
|
||||
{% for link in links_download %}
|
||||
<li><a href="{{ link.url }}" target="_blank">{% trans link.name %}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if GEOSERVER_BASE_URL %}
|
||||
{% get_obj_perms request.user for resource.layer as "layer_perms" %}
|
||||
{% endif %}
|
||||
|
||||
<li class="list-group-item">
|
||||
<a href="{% url "layer_metadata_detail" resource.typename %}">
|
||||
<button class="btn btn-primary btn-md btn-block">{% trans "Metadata Detail" %}</button>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{% if "change_resourcebase_metadata" in perms_list or "change_resourcebase" in perms_list or "delete_resourcebase" in perms_list or "change_layer_style" in layer_perms %}
|
||||
<li class="list-group-item">
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#edit-layer">{% trans "Edit Layer" %}</button>
|
||||
</li>
|
||||
<div class="modal fade" id="edit-layer" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Edit Layer" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row edit-modal">
|
||||
{% if "change_resourcebase_metadata" in perms_list %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-list-alt fa-3x"></i>
|
||||
<h4>{% trans "Metadata" %}</h4>
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_metadata" resource.service_typename %}">{% trans "Edit" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if GEOSERVER_BASE_URL and not resource.service %}
|
||||
{% if "change_layer_style" in layer_perms %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-tint fa-3x"></i>
|
||||
<h4>{% trans "Styles" %}</h4>
|
||||
{% if preview == 'geoext' %}
|
||||
<a class="btn btn-default btn-block btn-xs style-edit" data-dismiss="modal" href="#">{% trans "Edit" %}</a>
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_style_manage" resource.service_typename %}">{% trans "Manage" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if "change_resourcebase" in perms_list %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-photo fa-3x"></i>
|
||||
<h4>{% trans "Thumbnail" %}</h4>
|
||||
<a class="btn btn-default btn-block btn-xs" href="#" id="set_thumbnail">{% trans "Set" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if "change_resourcebase" in perms_list or "delete_resourcebase" in perms_list or "change_layer_data" in layer_perms %}
|
||||
<div class="col-sm-3">
|
||||
<i class="fa fa-square-o fa-3x rotate-45"></i>
|
||||
<h4>{% trans "Layer" %}</h4>
|
||||
{% if "change_resourcebase" in perms_list and not resource.service %}
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_replace" resource.service_typename %}">{% trans "Replace" %}</a>
|
||||
{% endif %}
|
||||
{% if resource.storeType == 'dataStore' and "change_layer_data" in layer_perms %}
|
||||
<a class="btn btn-default btn-block btn-xs" href="{% url "new_map" %}?layer={{resource.service_typename}}">{% trans "Edit data" %}</a>
|
||||
{% endif %}
|
||||
{% if "delete_resourcebase" in perms_list %}
|
||||
<a class="btn btn-danger btn-block btn-xs" href="{% url "layer_remove" resource.service_typename %}">{% trans "Remove" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<li class="list-group-item">
|
||||
<button class="btn btn-default btn-md btn-block" data-toggle="modal" data-target="#download-metadata">{% trans "Download Metadata" %}</button>
|
||||
</li>
|
||||
|
||||
<div class="modal fade" id="download-metadata" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{% trans "Download Metadata" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div style="margin-bottom:20px">
|
||||
<h5 class="modal-title" id="myModalLabel">
|
||||
Full metadata
|
||||
</h5>
|
||||
<ul>
|
||||
<li><a target="_blank" href="../catalogue/csw_to_extra_format/{{resource.uuid}}/{{resource.title}}.txt"> Text format </a></li>
|
||||
<li><a target="_blank" href="../catalogue/csw_to_extra_format/{{resource.uuid}}/{{resource.title}}.html"> HTML format </a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="margin-bottom:20px">
|
||||
<h5 class="modal-title" id="myModalLabel">
|
||||
Standard Metadata - XML format
|
||||
</h5>
|
||||
<ul>
|
||||
{% for link in metadata %}
|
||||
<li><a href="{{ link.url }}" target="_blank">{{ link.name }}</a></li>
|
||||
{% endfor %}
|
||||
<ul>
|
||||
</div>
|
||||
{% if resource.storeType == 'dataStore' %}
|
||||
<h5 class="modal-title" id="myModalLabel">Attribute Information</h5>
|
||||
<ul>
|
||||
<li><a href="{{ resource.get_absolute_url }}/feature_catalogue" target="_blank">ISO Feature Catalogue</a></li>
|
||||
<ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if resource.get_legend %}
|
||||
<li class="list-group-item">
|
||||
<h4 class="list-group-item-heading">{%trans "Legend" %}</h4>
|
||||
<p>{{ style.sld_title }}</p>
|
||||
{% if resource.get_legend.link_type == 'image' %}
|
||||
{% if request.user.is_authenticated and 'access_token' in request.session %}
|
||||
<p><img id="legend_icon" src="{{ resource.get_legend.url }}&access_token={{ request.session.access_token }}"></p>
|
||||
{% else %}
|
||||
<p><img id="legend_icon" src="{{ resource.get_legend.url }}"></p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div id='legend_placeholder'></div>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Maps using this layer" %}</h4>
|
||||
{% if resource.maps %}
|
||||
<p>{% trans "List of maps using this layer:" %}</p>
|
||||
{% endif %}
|
||||
<ul class="list-unstyled">
|
||||
{% for maplayer in resource.maps %}
|
||||
<li><a href="{{ maplayer.map.get_absolute_url }}">{{ maplayer.map.title }}</a></li>
|
||||
{% empty %}
|
||||
<li>{% trans "This layer is not currently used in any maps." %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Create a map using this layer" %}</h4>
|
||||
<p>{% trans "Click the button below to generate a new map based on this layer." %}</p>
|
||||
<a href="{% url "new_map" %}?layer={{resource.service_typename}}" class="btn btn-primary btn-block">{% trans "Create a Map" %}</a>
|
||||
</li>
|
||||
|
||||
{% if documents.count > 0 %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Documents related to this layer" %}</h4>
|
||||
<p>{% trans "List of documents related to this layer:" %}</p>
|
||||
<ul class="list-unstyled">
|
||||
{% for document in documents %}
|
||||
<li><a href="{{ document.get_absolute_url }}">{{ document.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Styles" %}</h4>
|
||||
<p>{% trans "The following styles are associated with this layer. Choose a style to view it in the preview map." %}</p>
|
||||
<ul class="list-unstyled">
|
||||
{% for style in resource.styles.all %}
|
||||
<li>
|
||||
{% if resource.default_style == style %}
|
||||
<input type="radio" checked name="style" id="{{ style.name }}" value="{{ style.title }}"/>
|
||||
(default style)
|
||||
{% else %}
|
||||
<input type="radio" name="style" id="{{ style.name }}" value="{{ style.title }}"/>
|
||||
{% endif %}
|
||||
<a href="{{ GEOSERVER_BASE_URL }}{{ style.absolute_url }}" >{{ style.sld_title }}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>{% trans "No styles associated with this layer" %}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% if "change_resourcebase_permissions" in perms_list %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Permissions" %}</h4>
|
||||
<p>{% trans "Click the button below to change the permissions of this layer." %}</p>
|
||||
<p><a href="#modal_perms" data-toggle="modal" class="btn btn-primary btn-block" data-target="#_permissions">{% trans "Change Layer Permissions" %}</a></p>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if resource.storeType == "remoteStore" %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "External service layer" %}</h4>
|
||||
<div>{% trans "Source" %}: <a href="/services/{{resource.service.id}}">{{ resource.service.title }}</a></div>
|
||||
<div>{% trans "Type" %}: {{ resource.service.type }}</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% include "base/_resourcebase_contact_snippet.html" %}
|
||||
</ul>
|
||||
|
||||
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% include "_permissions_form.html" %}
|
||||
{% endif %}
|
||||
|
||||
</div> <!-- col-md-4 -->
|
||||
</div> <!-- row -->
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
{% with "_geogig_scripts.html" as geogig_scripts_template %}
|
||||
{% include geogig_scripts_template %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% user_rating_js request.user resource "layer" %}
|
||||
{% else %}
|
||||
{% overall_rating resource 'layer' as the_layer_rating %}
|
||||
{% endif %}
|
||||
{% include 'rating.html' %}
|
||||
{% include 'request_download.html' %}
|
||||
<script type="text/javascript">
|
||||
$("#comment_submit_btn").click(function(event) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $("#form_post_comment").attr('action'),
|
||||
data: $("#form_post_comment").serialize(),
|
||||
success: function() {
|
||||
$('#form_post_comment_div').modal().find('form')[0].reset();
|
||||
$('#form_post_comment_div').modal('hide');
|
||||
$('#comments_section').load(window.location.pathname + ' #comments_section',
|
||||
function(){$(this).children().unwrap()})
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
{% if resource.get_legend.link_type == 'json' %}
|
||||
$.getJSON('{{resource.get_legend_url}}', function (data) {
|
||||
var legend = data.layers[0].legend;
|
||||
var items = [];
|
||||
$.each(legend, function (key, dvalue) {
|
||||
items.push('<li><img src="data:image/png;base64,' + dvalue.imageData + '">'+dvalue.label+'</li>');
|
||||
});
|
||||
|
||||
$('<ul/>', {
|
||||
'class': 'arcgis-legend',
|
||||
html: items.join('')
|
||||
|
||||
}).appendTo('#legend_placeholder');
|
||||
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
$('#set_thumbnail').click(function(){
|
||||
createMapThumbnail();
|
||||
$('#edit-layer').modal('toggle');
|
||||
});
|
||||
</script>
|
||||
{% if GEONODE_SECURITY_ENABLED %}
|
||||
{% include "_permissions_form_js.html" %}
|
||||
{% endif %}
|
||||
{% endblock extra_script %}
|
||||
@@ -9,7 +9,17 @@
|
||||
{% load guardian_tags %}
|
||||
|
||||
{% block title %}{{ block.super }} {% trans "Layers" %} | {{ resource.title|default:resource.typename }}{% endblock %}
|
||||
{% block page-title %}{% trans "Explore Layers" %}{% endblock %}
|
||||
{% block page-title %}
|
||||
{% trans "Explore Layers" %}
|
||||
<div class="btn-group pull-right">
|
||||
<a class="btn btn-primary btn-icon" href="{% url "layer_browse" %}">
|
||||
<i class="fa fa-bars" title="All Layers"></i>
|
||||
</a>
|
||||
<a class="btn btn-primary" href="{% url "layer_browse" %}">
|
||||
{% trans "All Layers" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
|
||||
@@ -38,16 +48,16 @@
|
||||
|
||||
{% block subheader %}
|
||||
<section id="subheader">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ resource.title|default:resource.typename }}</h2>
|
||||
<p>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>{{ resource.title|default:resource.typename }}</h2>
|
||||
<p>
|
||||
{{ resource.service_type }}
|
||||
{% if resource.category %}
|
||||
{% trans "in" %} <a href="{% url "search" %}?category__identifier__in={{ resource.category.identifier }}">{{ resource.category }}</a> |
|
||||
{% trans "in" %} <a href="{% url "search" %}?category__identifier__in={{ resource.category.identifier }}">{{ resource.category }}</a> |
|
||||
{% endif %}
|
||||
{% if resource.date %}
|
||||
{% if resource.date %}
|
||||
{% if resource.date_type == 'creation' %} {% trans "Created on" %} {{ resource.date }}
|
||||
{% elif resource.date_type == 'publication' %} {% trans "Published on" %} {{ resource.date }}
|
||||
{% else %} {% trans "Updated on" %} {{ resource.date }}
|
||||
@@ -59,13 +69,14 @@
|
||||
{% include "social_links.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block body_outer %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
|
||||
@@ -227,11 +238,6 @@
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article id="charts" class="tab-pane">
|
||||
<h4>No charts available at this time</h4>
|
||||
<a href="/table/fields?lyrname={{ resource.typename }}">Create Chart</a>
|
||||
</article>
|
||||
|
||||
<article id="comments" class="tab-pane">
|
||||
{% with resource as obj %}
|
||||
{% include "_comments.html" %}
|
||||
@@ -271,8 +277,8 @@
|
||||
{% if resource.storeType != "remoteStore" %}
|
||||
<li class="list-group-item">
|
||||
{% if links or links_download %}
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#download-layer">{% trans "Download Layer" %}</button>
|
||||
{% else %}
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="modal" data-target="#download-layer">{% trans "Download Layer" %}</button>
|
||||
{% else %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<button class="btn btn-primary btn-md btn-block" id="request-download">{% trans "Request Download" %}</button>
|
||||
{% endif %}
|
||||
@@ -482,6 +488,186 @@
|
||||
<a href="{% url "new_map" %}?layer={{resource.service_typename}}" class="btn btn-primary btn-block">{% trans "Create a Map" %}</a>
|
||||
</li>
|
||||
|
||||
<!-- Charts section -->
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Charts using this layer" %}</h4>
|
||||
{% if resource.maps %}
|
||||
<p>{% trans "List of charts with this layer:" %}</p>
|
||||
{% endif %}
|
||||
<ul class="list-unstyled">
|
||||
{% for chart in resource.chart_set.all %}
|
||||
<li><a href="{% url 'chart_detail' chart.id %}">{{ chart.get_type_display }}</a>, Category field = {{chart.category}}, Quantity field = {{chart.quantity}}</li>
|
||||
{% empty %}
|
||||
<li><p>No charts available at this time</p></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% if "download_resourcebase" in perms_list %}
|
||||
<li class="list-group-item" id="create-chart">
|
||||
<h4>{% trans "Create a chart using this layer" %}</h4>
|
||||
<p>{% trans "Click the button below to generate a new chart based on this layer, choosing a quantity and a category field." %}</p>
|
||||
<button class="btn btn-primary btn-md btn-block" data-toggle="collapse" data-target="#form" onclick="updateLinkAndTitle();">Create a chart</button>
|
||||
<div id="form" class="collapse">
|
||||
<form name="f" method="post" class="form-horizontal" onchange="updateLinkAndTitle();" action="/chart/add/{{ resource.id }}/">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<input id="id_layer" name="layer" value="{{ resource.id }}" type="hidden">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_category">Category:</label>
|
||||
<div class="col-md-8">
|
||||
<select class="form-control" id="id_category" name="category" >
|
||||
{% for attribute in resource.attributes %}
|
||||
<option value={{ attribute.attribute }}>{{ attribute.attribute }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_quantity">Quantity:</label>
|
||||
<div class="col-md-8">
|
||||
<select class="form-control" id="id_quantity" name="quantity">
|
||||
{% for attribute in resource.attributes %}
|
||||
{% if 'gml:' not in attribute.attribute_type and 'string' not in attribute.attribute_type%}
|
||||
<option value={{ attribute.attribute }}>{{ attribute.attribute }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_type">Type:</label>
|
||||
<div class="col-md-8">
|
||||
<select class="form-control" id="id_type" name="type">
|
||||
<option value="0" selected="selected">Bar chart</option>
|
||||
<option value="1">Pie chart</option>
|
||||
<option value="2">Donut chart</option>
|
||||
<option value="3">Line chart</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_aggr_type">Aggregation:</label>
|
||||
<div class="col-md-8">
|
||||
<select class="form-control" id="id_aggr_type" name="aggr_type">
|
||||
<option value="0">Sum</option>
|
||||
<option value="1">Mean</option>
|
||||
<option value="2">Category count</option>
|
||||
<option value="3">Max</option>
|
||||
<option value="4">Min</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_title">Title:</label>
|
||||
<div class="col-md-8">
|
||||
<input id="id_title" maxlength="128" name="title" type="text" class="form-control" >
|
||||
<p style="font-size: small; margin:0">Suggested title:</p>
|
||||
<p id="title" style="font-size: small; margin:0">Testo di prova</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="id_abstract">Abstract:</label>
|
||||
<div class="col-md-8">
|
||||
<textarea class="form-control" cols="40" id="id_abstract" name="abstract" rows="5"></textarea>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-block" data-toggle="modal" data-target="#chart_popup" onclick="get_chart_popup()">Preview</button>
|
||||
<button type="submit" class="btn btn-primary btn-block" onclick="title_eval();">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
<div id="chart_popup" class="modal fade" role="dialog" >
|
||||
<script src="/static/js/csv.js"></script>
|
||||
<script src="/static/js/donut.js"></script>
|
||||
<script src="/static/js/bar.js"></script>
|
||||
<script src="/static/js/pie.js"></script>
|
||||
<script src="/static/js/line.js"></script>
|
||||
<script src="/static/js/getChart.js"></script>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script src="https://cdn.rawgit.com/eligrey/canvas-toBlob.js/f1a01896135ab378aa5c0118eadd81da55e698d8/canvas-toBlob.js"></script>
|
||||
<script src="https://cdn.rawgit.com/eligrey/FileSaver.js/e9d941381475b5df8b7d7691013401e171014e89/FileSaver.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/index.css">
|
||||
<div class="modal-dialog" id="chart" style="width: 55%">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 id="chart_title" class="modal-title"></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="chart_area" style="display: table; float: inherit; width: 50%;">
|
||||
</div>
|
||||
<div>
|
||||
<h5>Abstract</h5>
|
||||
<p id="abstract"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var typename = "{{ resource.typename }}";
|
||||
var lyr;
|
||||
var category;
|
||||
var quantity;
|
||||
var agg;
|
||||
var chart_type;
|
||||
var csv_url;
|
||||
var suggestedTitle = "";
|
||||
|
||||
|
||||
function update_link(){
|
||||
csv_url = '/table?lyrname=' + typename;
|
||||
category = document.f.category.value;
|
||||
csv_url = csv_url + '&category=' + category;
|
||||
quantity = document.f.quantity.value;
|
||||
csv_url = csv_url + '&quantity=' + quantity;
|
||||
agg = document.f.aggr_type.value;
|
||||
chart_type = document.f.type.value;
|
||||
return;
|
||||
}
|
||||
|
||||
function attachAbstract(){
|
||||
var abstractText = document.getElementById("id_abstract").value;
|
||||
document.getElementById('abstract').innerHTML = abstractText;
|
||||
}
|
||||
|
||||
|
||||
function chart_title(){
|
||||
if (agg == dict_title_agg[2]){
|
||||
suggestedTitle = dict_title_agg[agg] + " of elements for " + category;
|
||||
} else {
|
||||
var title = dict_title_agg[agg]
|
||||
suggestedTitle = title + " of " + quantity + " for " + category;
|
||||
}
|
||||
document.getElementById("title").innerHTML = suggestedTitle;
|
||||
return suggestedTitle;
|
||||
};
|
||||
|
||||
|
||||
function title_eval(){
|
||||
if (document.getElementById("id_title").textLength == 0){
|
||||
document.getElementById("id_title").value = suggestedTitle;
|
||||
}
|
||||
}
|
||||
|
||||
function updateLinkAndTitle(){
|
||||
update_link();
|
||||
chart_title();
|
||||
attachAbstract();
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
{% if documents.count > 0 %}
|
||||
<li class="list-group-item">
|
||||
<h4>{% trans "Documents related to this layer" %}</h4>
|
||||
@@ -547,6 +733,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
{{ block.super }}
|
||||
|
||||
{% if GEOGIG_ENABLED and resource.link_set.geogig %}
|
||||
{% with "_geogig_scripts.html" as geogig_scripts_template %}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url "layer_browse" %}">{% trans "Layers" %}</a></li>
|
||||
<li><a href="{% url "document_browse" %}">{% trans "Documents" %}</a></li>
|
||||
<li><a href="{% url "chart_list" %}">{% trans "Charts" %}</a></li>
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="/uploads">{% trans "Uploads" %}</a></li>
|
||||
<li><a href="{% url "services" %}">{% trans "Remote Services" %}</a></li>
|
||||
@@ -47,79 +48,79 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li id="nav_maps">
|
||||
{% if user.is_authenticated %}
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Maps" %}<i class="fa fa-angle-down fa-lg"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url "maps_browse" %}">{% trans "Explore Maps" %}</a></li>
|
||||
<li> <a href="{% url "new_map" %}">{% trans "Create Map" %}</a></li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<a href="{% url "maps_browse" %}">{% trans "Maps" %}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "About" %}<i class="fa fa-angle-down fa-lg"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url "profile_browse" %}">{% trans "People" %}</a></li>
|
||||
<li><a href="{% url "group_list" %}">{% trans "Groups" %}</a></li>
|
||||
{% if perms.announcements.can_manage %}
|
||||
<li><a href="{% url "announcements_list" %}">{% trans "Announcements" %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.is_superuser %}
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="/admin/people/profile/add/">{% trans "Add User" %}</a></li>
|
||||
<li><a href="{% url "account_invite_user" %}">{% trans "Invite User" %}</a></li>
|
||||
<li><a href="{% url "group_create" %}">{% trans "Create Group" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% block extra_tab %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<div class="search">
|
||||
<form id="search" action="{% url "search" %}" >
|
||||
<span class="fa fa-search"></span>
|
||||
{% if HAYSTACK_SEARCH %}
|
||||
<input id="search_input" type="text" placeholder='{% trans "Search" %}' name="q">
|
||||
{% else %}
|
||||
<input id="search_input" type="text" placeholder='{% trans "Search" %}' name="title__icontains">
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
{% if not user.is_authenticated and REGISTRATION_OPEN %}
|
||||
<li><a href="{% url "account_signup" %}">{% trans "Register" %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="#" class="dropdown-toggle avatar" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% avatar user 42 %} {{ user.username }}<i class="fa fa-angle-down fa-lg"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ user.get_absolute_url }}">{% trans "Profile" %}</a></li>
|
||||
<li><a href="{% url "recent-activity" %}">{% trans "Recent Activity" %}</a></li>
|
||||
<li><a href="{% url "messages_inbox" %}">{% trans "Inbox" %}</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{% url "admin:index" %}">Admin</a></li>
|
||||
<li><a href="{{ GEOSERVER_BASE_URL }}">GeoServer</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{% url "help" %}">{% trans "Help" %}</li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="/account/logout/">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Maps" %}<i class="fa fa-angle-down fa-lg"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url "maps_browse" %}">{% trans "Explore Maps" %}</a></li>
|
||||
<li> <a href="{% url "new_map" %}">{% trans "Create Map" %}</a></li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<li><a href="#" data-toggle="modal" data-target="#SigninModal" role="button" >{% trans "Sign in" %} </a></li>
|
||||
<a href="{% url "maps_browse" %}">{% trans "Maps" %}</a>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div> <!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
{% endblock header %}
|
||||
<!-- End of Navbar -->
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "About" %}<i class="fa fa-angle-down fa-lg"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{% url "profile_browse" %}">{% trans "People" %}</a></li>
|
||||
<li><a href="{% url "group_list" %}">{% trans "Groups" %}</a></li>
|
||||
{% if perms.announcements.can_manage %}
|
||||
<li><a href="{% url "announcements_list" %}">{% trans "Announcements" %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.is_superuser %}
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="/admin/people/profile/add/">{% trans "Add User" %}</a></li>
|
||||
<li><a href="{% url "account_invite_user" %}">{% trans "Invite User" %}</a></li>
|
||||
<li><a href="{% url "group_create" %}">{% trans "Create Group" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% block extra_tab %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<div class="search">
|
||||
<form id="search" action="{% url "search" %}" >
|
||||
<span class="fa fa-search"></span>
|
||||
{% if HAYSTACK_SEARCH %}
|
||||
<input id="search_input" type="text" placeholder='{% trans "Search" %}' name="q">
|
||||
{% else %}
|
||||
<input id="search_input" type="text" placeholder='{% trans "Search" %}' name="title__icontains">
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
{% if not user.is_authenticated and REGISTRATION_OPEN %}
|
||||
<li><a href="{% url "account_signup" %}">{% trans "Register" %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="#" class="dropdown-toggle avatar" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% avatar user 42 %} {{ user.username }}<i class="fa fa-angle-down fa-lg"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ user.get_absolute_url }}">{% trans "Profile" %}</a></li>
|
||||
<li><a href="{% url "recent-activity" %}">{% trans "Recent Activity" %}</a></li>
|
||||
<li><a href="{% url "messages_inbox" %}">{% trans "Inbox" %}</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{% url "admin:index" %}">Admin</a></li>
|
||||
<li><a href="{{ GEOSERVER_BASE_URL }}">GeoServer</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="{% url "help" %}">{% trans "Help" %}</li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="/account/logout/">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li><a href="#" data-toggle="modal" data-target="#SigninModal" role="button" >{% trans "Sign in" %} </a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div> <!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
{% endblock header %}
|
||||
<!-- End of Navbar -->
|
||||
|
||||
|
||||
{% block middle %}
|
||||
{% block middle %}
|
||||
<header>
|
||||
<div class="container">
|
||||
<h3>{% block page-title %}{% endblock %}</h3>
|
||||
@@ -132,12 +133,12 @@
|
||||
{% include "_messages.html" %}
|
||||
{% block body_outer %}
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% block sidebar %}{% endblock %}
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
{% block sidebar %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
+4
-1
@@ -8,6 +8,9 @@ urlpatterns = patterns('',
|
||||
url(r'^/?$',
|
||||
TemplateView.as_view(template_name='site_index.html'),
|
||||
name='home'),
|
||||
) + urlpatterns
|
||||
) + urlpatterns + [
|
||||
url(r'^chart/', include('charts_app.urls')),
|
||||
url(r'^table/', include('wfs_harvest.urls'))
|
||||
]
|
||||
|
||||
urlpatterns += importer_urlpatterns
|
||||
Whitespace-only changes.
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
Whitespace-only changes.
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
@@ -0,0 +1,41 @@
|
||||
<h1>Field choice</h1>
|
||||
<p>Choose category field</p>
|
||||
|
||||
<form name="f">
|
||||
<select name="category" onchange="update_link();update_add();">
|
||||
{% for fieldname in fieldnames %}
|
||||
<option value="{{ fieldname }}">{{ fieldname }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p>Choose quantity field</p>
|
||||
<select name="quantity" onchange="update_link();">
|
||||
{% for fieldname in num_fieldnames %}
|
||||
<option value="{{ fieldname }}">{{ fieldname }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<br>
|
||||
<br>
|
||||
<a href="/charts" id="link">Build Chart</a>
|
||||
<a href="/charts" id="add_chart">Add Chart</a>
|
||||
</form>
|
||||
<script>
|
||||
function update_link()
|
||||
{
|
||||
var url = '/chart/pie_chart_v2?lyrname={{ lyrname }}';
|
||||
url = url + '&category=' + document.f.category.value;
|
||||
url = url + '&quantity=' + document.f.quantity.value;
|
||||
document.getElementById('link').href = url;
|
||||
return;
|
||||
}
|
||||
update_link();
|
||||
|
||||
function update_add()
|
||||
{
|
||||
var url = '/chart/add?lyrname={{ lyrname }}';
|
||||
url = url + '&category=' + document.f.category.value;
|
||||
url = url + '&quantity=' + document.f.quantity.value;
|
||||
document.getElementById('add_chart').href = url;
|
||||
return;
|
||||
}
|
||||
update_add();
|
||||
</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
import urllib
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from . import views
|
||||
|
||||
# Create your tests here.
|
||||
class GetFieldsView(SimpleTestCase):
|
||||
def test_non_existing_lyr(self):
|
||||
# url = reverse(views.get_fields)
|
||||
enc_lyrname = urllib.urlencode({'lyrname': 'idontexist'})
|
||||
# complete_url = url + '?' + enc_lyrname
|
||||
# self.client.get('/table')
|
||||
# self.client.get(complete_url)
|
||||
self.assertTrue(False)
|
||||
|
||||
def test_non_existing_field(self):
|
||||
# self.client.get(reverse('wfs_harvest:get_fields'))
|
||||
self.assertTrue(False)
|
||||
@@ -0,0 +1,8 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.get_wfs_csv),
|
||||
url(r'^fields$', views.get_fields)
|
||||
]
|
||||
@@ -0,0 +1,56 @@
|
||||
import urllib
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from geonode.layers.models import Layer
|
||||
|
||||
|
||||
def get_fields(layer_id):
|
||||
layer = Layer.objects.get(pk=layer_id)
|
||||
lyrname = layer.typename
|
||||
params = urllib.urlencode({
|
||||
'service':'WFS',
|
||||
'version':'1.1.0',
|
||||
'request':'DescribeFeatureType',
|
||||
'typename':lyrname
|
||||
})
|
||||
|
||||
wfs_request = urllib.urlopen('http://localhost/geoserver/ows?%s'
|
||||
% params)
|
||||
content = wfs_request.read()
|
||||
|
||||
root = ET.fromstring(content)
|
||||
namespace = root.tag.split('}')[0] + '}'
|
||||
xpath = ('./{0}complexType/{0}complexContent/'
|
||||
'{0}extension/{0}sequence/{0}element').format(namespace)
|
||||
numeric_types = ['byte', 'decimal', 'int', 'integer', 'long', 'short',
|
||||
'boolean', 'double', 'float']
|
||||
fields = {}
|
||||
for element in root.findall(xpath):
|
||||
type = element.attrib['type']
|
||||
if not type.startswith('gml:'):
|
||||
fields[element.attrib['name']] = type[4:]
|
||||
|
||||
fieldnames = fields.keys()
|
||||
num_fieldnames = [k for k, v in fields.iteritems() if v in numeric_types]
|
||||
|
||||
return fieldnames, num_fieldnames
|
||||
|
||||
|
||||
def get_featno(layer_id):
|
||||
lyr = Layer.objects.get(pk=layer_id)
|
||||
wfs_baseurl = lyr.link_set.get(link_type='OGC:WFS').url
|
||||
|
||||
params = urllib.urlencode({
|
||||
'service': 'WFS',
|
||||
'version': '1.1.0',
|
||||
'request': 'GetFeature',
|
||||
'typename': lyr.typename,
|
||||
'resultType': 'hits'
|
||||
})
|
||||
|
||||
wfs_count_request = urllib.urlopen(wfs_baseurl + '?%s' % params)
|
||||
content = wfs_count_request.read()
|
||||
|
||||
root = ET.fromstring(content)
|
||||
|
||||
return int(root.attrib['numberOfFeatures'])
|
||||
@@ -0,0 +1,80 @@
|
||||
import urllib
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render
|
||||
from django.conf import settings
|
||||
|
||||
from geonode.layers.models import Layer
|
||||
|
||||
from utils import get_featno
|
||||
|
||||
|
||||
def get_fields(request):
|
||||
lyrname = request.GET['lyrname']
|
||||
params = urllib.urlencode({
|
||||
'service':'WFS',
|
||||
'version':'1.1.0',
|
||||
'request':'DescribeFeatureType',
|
||||
'typename':lyrname
|
||||
})
|
||||
|
||||
wfs_request = urllib.urlopen('http://localhost/geoserver/ows?%s'
|
||||
% params)
|
||||
content = wfs_request.read()
|
||||
|
||||
root = ET.fromstring(content)
|
||||
namespace = root.tag.split('}')[0] + '}'
|
||||
xpath = ('./{0}complexType/{0}complexContent/'
|
||||
'{0}extension/{0}sequence/{0}element').format(namespace)
|
||||
numeric_types = ['byte', 'decimal', 'int', 'integer', 'long', 'short',
|
||||
'boolean', 'double', 'float']
|
||||
fields = {}
|
||||
for element in root.findall(xpath):
|
||||
type = element.attrib['type']
|
||||
if not type.startswith('gml:'):
|
||||
fields[element.attrib['name']] = type[4:]
|
||||
|
||||
fieldnames = fields.keys()
|
||||
num_fieldnames = [k for k, v in fields.iteritems() if v in numeric_types]
|
||||
|
||||
context = {
|
||||
'lyrname': lyrname,
|
||||
'fieldnames': fieldnames,
|
||||
'num_fieldnames': num_fieldnames
|
||||
}
|
||||
return render(request, 'wfs_harvest/field_choice.html', context)
|
||||
|
||||
|
||||
def get_wfs_csv(request):
|
||||
qdict = request.GET
|
||||
typename = qdict['lyrname']
|
||||
category_field = qdict['category']
|
||||
quantity_field = qdict['quantity']
|
||||
|
||||
lyr = Layer.objects.get(typename=typename)
|
||||
featno = get_featno(lyr.id)
|
||||
if featno > settings.MAX_CSV_RECORDS:
|
||||
response = HttpResponse('{0} has too many records; csv download'
|
||||
' is not available for very large '
|
||||
'datasets'.format(lyr.title))
|
||||
response.status_code = 413
|
||||
response.reason_phrase = 'REQUEST ENTITY TOO LARGE'
|
||||
return response
|
||||
|
||||
params = urllib.urlencode({
|
||||
'service':'WFS',
|
||||
'version':'1.1.0',
|
||||
'request':'GetFeature',
|
||||
'typename':typename,
|
||||
'propertyName':category_field + ',' + quantity_field,
|
||||
'outputFormat':'csv',
|
||||
'maxFeatures': str(settings.MAX_CSV_RECORDS)
|
||||
})
|
||||
|
||||
wfs_baseurl = lyr.link_set.get(link_type='OGC:WFS').url
|
||||
wfs_request = urllib.urlopen(wfs_baseurl + '?%s' % params)
|
||||
content = wfs_request.read()
|
||||
response = HttpResponse(content, content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="myfile.csv"'
|
||||
return response
|
||||
Reference in new issue
Block a user