diff --git a/.gitignore b/.gitignore
index b903977..9ce3160 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,3 +65,6 @@ geoserver/
uploaded/
development.db
development.db-journal
+
+# PyCharm
+.idea
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
{% blocktrans %}GeoNode is an open source platform for sharing geospatial data and maps.{% endblocktrans %}
+{% blocktrans %}If you have any questions about the software or service, join our mailing list.{% endblocktrans %}
+ {% trans 'Need help?' %} {% trans 'Getting Started' %} +D3js version 4, use canvas
+ + + + \ No newline at end of file diff --git a/charts_app/templates/donut_chart_v2.html b/charts_app/templates/donut_chart_v2.html new file mode 100644 index 0000000..c371272 --- /dev/null +++ b/charts_app/templates/donut_chart_v2.html @@ -0,0 +1,169 @@ + + +D3js version 3, use svg
+ + + + + \ No newline at end of file diff --git a/charts_app/templates/pie_chart_v1.html b/charts_app/templates/pie_chart_v1.html new file mode 100644 index 0000000..10e08c5 --- /dev/null +++ b/charts_app/templates/pie_chart_v1.html @@ -0,0 +1,65 @@ + + +D3js version 4, use svg
+ + + + diff --git a/charts_app/templates/pie_chart_v2.html b/charts_app/templates/pie_chart_v2.html new file mode 100644 index 0000000..7a93002 --- /dev/null +++ b/charts_app/templates/pie_chart_v2.html @@ -0,0 +1,67 @@ + + +D3js version 4, use svg
+ + + + diff --git a/charts_app/tests.py b/charts_app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/charts_app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/charts_app/urls.py b/charts_app/urls.py new file mode 100644 index 0000000..2923a2d --- /dev/null +++ b/charts_app/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import url + +from . import views + +urlpatterns = [url(r'^(?P+ {% blocktrans with object.title as object_title %} + Are you sure you want to remove {{ object_title }} chart? + {% endblocktrans %} +
+ ++ +
+ {% block social_links %} + {% if SOCIAL_BUTTONS %} + {% include "social_links.html" %} + {% endif %} + {% endblock %} +{{ object.abstract }}
++ +
+ {% block social_links %} + {% if SOCIAL_BUTTONS %} + {% include "social_links.html" %} + {% endif %} + {% endblock %} +{{ object.abstract }}
++ {{ resource.service_type }} + {% if resource.category %} + {% trans "in" %} {{ resource.category }} | + {% 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 %} +
+ {% block social_links %} + {% if SOCIAL_BUTTONS %} + {% include "social_links.html" %} + {% endif %} + {% endblock %} +| {% trans "Dimension" %} | +{% trans "Enabled" %} | +{% trans "Regex" %} | +
|---|---|---|
| {% trans "TIME" %} | +{% if resource.has_time %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %} | +{% if resource.time_regex %}{{ resource.time_regex }}{% else %} {% endif %} | +
| {% trans "Attribute Name" %} | +{% trans "Label" %} | +{% trans "Description" %} | + {% if wps_enabled %} +{% trans "Range" %} | +{% trans "Average" %} | +{% trans "Median" %} | +{% trans "Standard Deviation" %} | + {% endif %} +|
|---|---|---|---|---|---|---|---|
| {{ attribute }} | +{{ attribute.attribute_label|default:"" }} | +{{ attribute.description|default:"" }} | + {% if wps_enabled %} + {% if attribute.unique_values == "NA" %} +{{ attribute.unique_values }} | + {% else %} ++ + | + {% endif %} +{{ attribute.average|floatformat:"2" }} | +{{ attribute.median|floatformat:"2" }} | +{{ attribute.stddev|floatformat:"2" }} | + {% endif %} +
+ 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.
+ {{ style.sld_title }}
+ {% if resource.get_legend.link_type == 'image' %} + {% if request.user.is_authenticated and 'access_token' in request.session %} +{% trans "List of maps using this layer:" %}
+ {% endif %} +{% trans "Click the button below to generate a new map based on this layer." %}
+ {% trans "Create a Map" %} +{% trans "List of documents related to this layer:" %}
+{% trans "The following styles are associated with this layer. Choose a style to view it in the preview map." %}
+{% trans "Click the button below to change the permissions of this layer." %}
+ ++ {{ resource.service_type }} + {% if resource.category %} + {% trans "in" %} {{ resource.category }} | + {% 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 %} +
+ {% block social_links %} + {% if SOCIAL_BUTTONS %} + {% include "social_links.html" %} + {% endif %} + {% endblock %} +| {% trans "Dimension" %} | +{% trans "Enabled" %} | +{% trans "Regex" %} | +
|---|---|---|
| {% trans "TIME" %} | +{% if resource.has_time %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %} | +{% if resource.time_regex %}{{ resource.time_regex }}{% else %} {% endif %} | +
| {% trans "Attribute Name" %} | +{% trans "Label" %} | +{% trans "Description" %} | + {% if wps_enabled %} +{% trans "Range" %} | +{% trans "Average" %} | +{% trans "Median" %} | +{% trans "Standard Deviation" %} | + {% endif %} +|
|---|---|---|---|---|---|---|---|
| {{ attribute }} | +{{ attribute.attribute_label|default:"" }} | +{{ attribute.description|default:"" }} | + {% if wps_enabled %} + {% if attribute.unique_values == "NA" %} +{{ attribute.unique_values }} | + {% else %} ++ + | + {% endif %} +{{ attribute.average|floatformat:"2" }} | +{{ attribute.median|floatformat:"2" }} | +{{ attribute.stddev|floatformat:"2" }} | + {% endif %} +
{{ style.sld_title }}
+ {% if resource.get_legend.link_type == 'image' %} + {% if request.user.is_authenticated and 'access_token' in request.session %} +{% trans "List of maps using this layer:" %}
+ {% endif %} +{% trans "Click the button below to generate a new map based on this layer." %}
+ {% trans "Create a Map" %} +{% trans "List of documents related to this layer:" %}
+{% trans "The following styles are associated with this layer. Choose a style to view it in the preview map." %}
+{% trans "Click the button below to change the permissions of this layer." %}
+ ++
{{ resource.service_type }} {% if resource.category %} - {% trans "in" %} {{ resource.category }} | + {% trans "in" %} {{ resource.category }} | {% 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 %} -
{% trans "List of charts with this layer:" %}
+ {% endif %} +No charts available at this time
{% trans "Click the button below to generate a new chart based on this layer, choosing a quantity and a category field." %}
+ +Choose category field
+ + + \ No newline at end of file diff --git a/wfs_harvest/tests.py b/wfs_harvest/tests.py new file mode 100644 index 0000000..fb01967 --- /dev/null +++ b/wfs_harvest/tests.py @@ -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) \ No newline at end of file diff --git a/wfs_harvest/urls.py b/wfs_harvest/urls.py new file mode 100644 index 0000000..c29d41e --- /dev/null +++ b/wfs_harvest/urls.py @@ -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) + ] diff --git a/wfs_harvest/utils.py b/wfs_harvest/utils.py new file mode 100644 index 0000000..c599214 --- /dev/null +++ b/wfs_harvest/utils.py @@ -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']) diff --git a/wfs_harvest/views.py b/wfs_harvest/views.py new file mode 100644 index 0000000..6c4330e --- /dev/null +++ b/wfs_harvest/views.py @@ -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