Skip to content

Commit 5d24a18

Browse files
committed
QGIS Server Related PR: Add geonode.qgis_server module.
This commit add geonode.qgis_server module as an alternative backend. This commit also contains necessary changes to help integrate this module into geonode, such as: * Configured Travis to run unittests for both GeoServer and QGIS Server in parallel * Configured development setup for QGIS Server in pavement.py * Modified unittests code branching to include QGIS Server unittests * Sample file of Django Settings to use QGIS Server * Code improvements to integrate QGIS Server backend to use Leaflet map viewer in layer view and maps view page * geonode.qgis_server module to integrate QGIS Server backend with GeoNode models and provides helpers to communicate with QGIS Server
1 parent dd1dbb8 commit 5d24a18

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+7868
-145
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ geonode/static/lib/css/leaflet.fullscreen.css
3636
geonode/static/lib/css/Leaflet.NavBar.css
3737
geonode/static/lib/css/leaflet-measure.css
3838
geonode/static/lib/css/leaflet-plugins.min.css
39+
geonode/static/lib/css/easy-button.css
3940
geonode/static/lib/css/bootstrap-tokenfield.css
4041
geonode/static/lib/js/assets.min.js
4142
geonode/static/lib/js/Control.Opacity.js
@@ -64,6 +65,8 @@ geonode/static/lib/js/waypoints.js
6465
geonode/static/lib/js/angular.min.js
6566
geonode/static/lib/js/angular.min.js.map
6667
geonode/static/lib/js/ZeroClipboard.min.js
68+
geonode/static/lib/js/clipboard.js
69+
geonode/static/lib/js/easy-button.js
6770
geonode/static/lib/js/moment.min.js
6871
geonode/static/lib/css/bootstrap-treeview.css
6972
geonode/static/lib/js/bootstrap-treeview.js

.travis.yml

+20-11
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,26 @@ services:
1313

1414
env:
1515
- BACKEND: 'geonode.geoserver'
16-
# FIXME. Will be uncommented when qgis_server backend exists
17-
# - BACKEND: 'geonode.qgis_server'
18-
# DOCKER_COMPOSE_VERSION: 1.11.2
19-
# # This is qgis server address
20-
# QGIS_SERVER_URL: http://localhost:9000/
21-
# # This is the location of docker network bridge
22-
# # So QGIS server can access this address
23-
# SITEURL: http://localhost:8000/
24-
# GEONODE_PROJECT_PATH: /home/travis/build/kartoza/geonode/
25-
# ON_TRAVIS: True
26-
# CELERY_ALWAYS_EAGER: True
16+
- BACKEND: 'geonode.qgis_server'
17+
DOCKER_COMPOSE_VERSION: 1.11.2
18+
# This is qgis server address
19+
QGIS_SERVER_URL: http://localhost:9000/
20+
# This is the location of docker network bridge
21+
# So QGIS server can access this address
22+
SITEURL: http://localhost:8000/
23+
QGIS_SERVER_PORT: 9000
24+
ON_TRAVIS: True
25+
CELERY_ALWAYS_EAGER: True
2726

2827
branches:
2928
only:
3029
- master
3130
- 2.7.x
3231
- 2.6.x
3332

33+
before_install:
34+
- scripts/misc/qgis_server_setup.sh before_install
35+
3436
install:
3537
- sudo apt-get -qq -y update
3638
- sudo apt-get install -y libgdal1h python-gdal gdal-bin spatialite-bin
@@ -46,12 +48,19 @@ install:
4648
- pip install codecov
4749

4850
before_script:
51+
# For QGIS Server Travis steps
52+
- scripts/misc/qgis_server_setup.sh before_script
53+
4954
- paver setup
5055

5156
script:
5257
- paver run_tests --coverage
5358

5459
after_script:
60+
# For QGIS Server Travis steps
61+
- scripts/misc/qgis_server_setup.sh after_script
62+
63+
# Cleanup
5564
- paver reset_hard
5665

5766
after_success:

docker-compose-qgis-server.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
version: '2'
2+
services:
3+
4+
qgis-server:
5+
image: kartoza/geonode_qgis-server
6+
volumes:
7+
# Mount this volume from current directory to
8+
# The absolute path, so these files were mounted
9+
# with the same path
10+
- './:${GEONODE_PROJECT_PATH}'
11+
environment:
12+
- QGIS_LOG_FILE=/tmp/qgis-server/qgis.log
13+
- QGIS_SERVER_LOG_FILE=/tmp/qgis-server/qgis-server.log
14+
- QGIS_DEBUG=5
15+
- QGIS_SERVER_LOG_LEVEL=5
16+
- QGIS_PLUGINPATH=/opt/qgis-server/plugins/
17+
# It is important to include this environment variable
18+
# QGIS-Server:LTR cgi needs it
19+
- QGIS_PROJECT_FILE=
20+
ports:
21+
- "${QGIS_SERVER_PORT}:80"
22+
network_mode: "bridge"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# -*- coding: utf-8 -*-
2+
#########################################################################
3+
#
4+
# Copyright (C) 2016 OSGeo
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
#
19+
#########################################################################
20+
21+
from django.core.management.base import BaseCommand
22+
23+
from geonode.layers.utils import delete_orphaned_layers
24+
25+
26+
class Command(BaseCommand):
27+
help = ("Delete orphaned layers.")
28+
29+
def handle(self, *args, **options):
30+
delete_orphaned_layers()

geonode/layers/models.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
from geonode.people.utils import get_valid_user
3636
from agon_ratings.models import OverallRating
3737
from geonode.utils import check_shp_columnnames
38-
from geonode.security.models import remove_object_permissions
38+
from geonode.security.models import (
39+
remove_object_permissions,
40+
PermissionLevelMixin)
3941

4042
logger = logging.getLogger("geonode.layers.models")
4143

@@ -59,7 +61,7 @@
5961
}
6062

6163

62-
class Style(models.Model):
64+
class Style(models.Model, PermissionLevelMixin):
6365

6466
"""Model for storing styles.
6567
"""
@@ -94,6 +96,16 @@ def absolute_url(self):
9496
self.name.encode('utf-8'))
9597
return None
9698

99+
def get_self_resource(self):
100+
"""Get associated resource base."""
101+
# Associate this model with resource
102+
try:
103+
layer = self.layer_styles.first()
104+
""":type: Layer"""
105+
return layer.get_self_resource()
106+
except:
107+
return None
108+
97109

98110
class LayerManager(ResourceBaseManager):
99111

geonode/layers/templates/layers/layer_detail.html

+46-14
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,12 @@ <h4 class="modal-title" id="myModalLabel">{% trans "Download Layer" %}</h4>
297297
</div>
298298
{% endif %}
299299
{% if links_download %}
300+
{% if not links %}
301+
{# if links doesn't exist, make this tab active by default #}
302+
<div class="tab-pane active" id="download_tab2">
303+
{% else %}
300304
<div class="tab-pane" id="download_tab2">
305+
{% endif %}
301306
</br>
302307
{% if layer_type == "vector" %}
303308
<button type="button" id = "button-toggle" class="btn btn-primary btn-circle" data-toggle="button" data-toggle="tooltip" title="Click to filter the layer">
@@ -453,6 +458,14 @@ <h4>{% trans "Styles" %}</h4>
453458
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_style_manage" resource.service_typename %}">{% trans "Manage" %}</a>
454459
</div>
455460
{% endif %}
461+
{% elif OGC_SERVER.default.BACKEND == 'geonode.qgis_server' and not resource.service %}
462+
{% if "change_layer_style" in layer_perms and preview == 'leaflet' %}
463+
<div class="col-sm-3">
464+
<i class="fa fa-tint fa-3x"></i>
465+
<h4>{% trans "Styles" %}</h4>
466+
<a class="btn btn-default btn-block btn-xs style-edit" data-dismiss="modal" href="#">{% trans "Edit" %}</a>
467+
</div>
468+
{% endif %}
456469
{% endif %}
457470
{% if "change_resourcebase" in perms_list %}
458471
<div class="col-sm-3">
@@ -468,7 +481,7 @@ <h4>{% trans "Layer" %}</h4>
468481
{% if "change_resourcebase" in perms_list and not resource.service %}
469482
<a class="btn btn-default btn-block btn-xs" href="{% url "layer_replace" resource.service_typename %}">{% trans "Replace" %}</a>
470483
{% endif %}
471-
{% if resource.storeType == 'dataStore' and "change_layer_data" in layer_perms %}
484+
{% if resource.storeType == 'dataStore' and "change_layer_data" in layer_perms and OGC_SERVER.default.BACKEND == 'geonode.geoserver' %}
472485
<a class="btn btn-default btn-block btn-xs" href="{% url "new_map" %}?layer={{resource.service_typename}}">{% trans "Edit data" %}</a>
473486
{% endif %}
474487
{% if "delete_resourcebase" in perms_list %}
@@ -598,19 +611,35 @@ <h4>{% trans "Documents related to this layer" %}</h4>
598611
<h4>{% trans "Styles" %}</h4>
599612
<p>{% trans "The following styles are associated with this layer. Choose a style to view it in the preview map." %}</p>
600613
<ul class="list-unstyled">
601-
{% for style in resource.styles.all %}
602-
<li>
603-
{% if resource.default_style == style %}
604-
<input type="radio" checked name="style" id="{{ style.name }}" value="{% if style.title %}{{ style.title }}{% else %}{{ style.name }}{% endif %}"/>
605-
(default style)
606-
{% else %}
607-
<input type="radio" name="style" id="{{ style.name }}" value="{% if style.title %}{{ style.title }}{% else %}{{ style.name }}{% endif %}"/>
608-
{% endif %}
609-
<a href="{{ GEOSERVER_BASE_URL }}{{ style.absolute_url }}" >{% if style.sld_title %}{{ style.sld_title }}{% else %}{{ style.name }}{% endif %}</a>
610-
</li>
611-
{% empty %}
612-
<li>{% trans "No styles associated with this layer" %}</li>
613-
{% endfor %}
614+
{% if OGC_SERVER.default.BACKEND == 'geonode.geoserver' %}
615+
{% for style in resource.styles.all %}
616+
<li>
617+
{% if resource.default_style == style %}
618+
<input type="radio" checked name="style" id="{{ style.name }}" value="{{ style.title }}"/>
619+
(default style)
620+
{% else %}
621+
<input type="radio" name="style" id="{{ style.name }}" value="{{ style.title }}"/>
622+
{% endif %}
623+
<a href="{{ GEOSERVER_BASE_URL }}{{ style.absolute_url }}" >{{ style.sld_title }}</a>
624+
</li>
625+
{% empty %}
626+
<li>{% trans "No styles associated with this layer" %}</li>
627+
{% endfor %}
628+
{% elif OGC_SERVER.default.BACKEND == 'geonode.qgis_server' %}
629+
{% for style in resource.qgis_layer.styles.all %}
630+
<li>
631+
{% if resource.qgis_layer.default_style == style %}
632+
<input type="radio" checked name="style" id="{{ style.name }}" value="{{ style.title }}"/>
633+
(default style)
634+
{% else %}
635+
<input type="radio" name="style" id="{{ style.name }}" value="{{ style.title }}"/>
636+
{% endif %}
637+
<a href="{{ style.style_url }}" >{{ style.title }}</a>
638+
</li>
639+
{% empty %}
640+
<li>{% trans "No styles associated with this layer" %}</li>
641+
{% endfor %}
642+
{% endif %}
614643
</ul>
615644
</li>
616645
{% endif %}
@@ -908,6 +937,9 @@ <h4>{% trans "External service layer" %}</h4>
908937
});
909938

910939
});
940+
{% if show_popup %}
941+
$('#download-layer').modal('toggle');
942+
{% endif %}
911943
</script>
912944

913945
{% if GEONODE_SECURITY_ENABLED %}

0 commit comments

Comments
 (0)