[gnome-maps/wip/jonasdn/geojson: 51/55] Add GeoJSONSource
- From: Jonas Danielsson <jonasdn src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-maps/wip/jonasdn/geojson: 51/55] Add GeoJSONSource
- Date: Fri, 23 Oct 2015 19:20:41 +0000 (UTC)
commit eb02d824e33b693e364d341a62de2dd3bc390510
Author: Jonas Danielsson <jonas threetimestwo org>
Date: Fri Oct 23 12:02:54 2015 +0200
Add GeoJSONSource
Add a Champlain::TileSource that creates tiles from a GeoJSON file.
This is based on the geojson-vt library from Mapbox.
src/geoJSONSource.js | 239 ++++++++++++++++++++++++++++++++++
src/org.gnome.Maps.src.gresource.xml | 1 +
2 files changed, 240 insertions(+), 0 deletions(-)
---
diff --git a/src/geoJSONSource.js b/src/geoJSONSource.js
new file mode 100644
index 0000000..b2878e7
--- /dev/null
+++ b/src/geoJSONSource.js
@@ -0,0 +1,239 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Jonas Danielsson <jonas threetimestwo org>
+ */
+
+const Cairo = imports.cairo;
+const Champlain = imports.gi.Champlain;
+const Clutter = imports.gi.Clutter;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+
+const geojsonvt = imports.index.geojsonvt;
+const Location = imports.location;
+const Place = imports.place;
+const PlaceMarker = imports.placeMarker;
+const Utils = imports.utils;
+
+const TileFeature = { POINT: 1,
+ LINESTRING: 2,
+ POLYGON: 3 };
+
+const GeoJSONSource = new Lang.Class({
+ Name: 'GeoJSONSource',
+ Extends: Champlain.TileSource,
+
+ _init: function(params) {
+ this.parent();
+
+ this._file = params.file;
+ this._mapView = params.mapView;
+ this._markerLayer = params.markerLayer;
+ this._bbox = new Champlain.BoundingBox();
+ },
+
+ get bbox() {
+ return this._bbox;
+ },
+
+ vfunc_get_tile_size: function() {
+ return 256;
+ },
+
+ vfunc_get_max_zoom_level: function() {
+ return 20;
+ },
+
+ vfunc_get_min_zoom_level: function() {
+ return 0;
+ },
+
+ vfunc_get_id: function() {
+ return 'geoJSONSource';
+ },
+
+ vfunc_get_name: function() {
+ return 'geoJSONSource';
+ },
+
+ vfunc_fill_tile: function(tile) {
+ if (tile.get_state() === Champlain.State.DONE)
+ return;
+
+ tile.connect('render-complete', (function(tile, data, size, error) {
+ if(!error) {
+ tile.set_state(Champlain.State.DONE);
+ tile.display_content();
+ }
+ else if(this.next_source)
+ this.next_source.fill_tile(tile);
+ }).bind(this));
+
+
+ Mainloop.idle_add(this._renderTile.bind(this, tile));
+ },
+
+ _isValid: function(coordinate) {
+ if (coordinate[0] > 180 || coordinate[0] < -180)
+ return false;
+
+ if (coordinate[1] > 90 || coordinate[1] < -90)
+ return false;
+
+ return true;
+ },
+
+ _compose: function(coordinates) {
+ for (let i = 0; i < coordinates.length; i++) {
+ if (this._isValid(coordinates[i]))
+ this._bbox.extend(coordinates[i][1], coordinates[i][0]);
+ else
+ return false;
+ }
+
+ return true;
+ },
+
+ _validateLineString: function(coordinates) {
+ return this._compose(coordinates);
+ },
+
+ _validatePolygon: function(coordinates) {
+ for (let i = 0; i < coordinates.length; i++)
+ if (!this._compose(coordinates[i]))
+ return false;
+
+ return true;
+ },
+
+ _validateFeature: function(feature) {
+ let geometry = feature.geometry;
+ if (!geometry)
+ return false;
+
+ if (geometry.type === "LineString") {
+ return this._validateLineString(geometry.coordinates);
+ } else if (geometry.type === "MultiLineString") {
+ for (let i = 0; i < geometry.coordinates.length; i++)
+ if (!this._validateLineString(geometry.coordinates[i]))
+ return false;
+ return true;
+ } else if (geometry.type === "Polygon") {
+ return this._validatePolygon(geometry.coordinates);
+ } else if (geometry.type === "MultiPolygon") {
+ for (let i = 0; i < geometry.coordinates.length; i++)
+ if (!this._validatePolygon(geometry.coordinates[i]))
+ return false;
+ return true;
+ } else if (geometry.type === "Point") {
+ let name = null;
+ if (feature.properties)
+ name = feature.properties.name;
+
+ let location = new Location.Location({
+ latitude: geometry.coordinates[1],
+ longitude: geometry.coordinates[0]
+ });
+
+ let place = new Place.Place({ name: name,
+ location: location });
+ let placeMarker = new PlaceMarker.PlaceMarker({ place: place,
+ mapView: this._mapView });
+ this._markerLayer.add_marker(placeMarker);
+ return true;
+ }
+
+ return false;
+ },
+
+ _validateInternal: function(root) {
+ if (!root || !root.type)
+ return false;
+
+ if (root.type === "FeatureCollection") {
+ for (let i = 0; i < root.features.length; i++)
+ if (!this._validateFeature(root.features[i]))
+ return false;
+ } else if (root.type === "Feature")
+ return this._validateFeature(root);
+
+ return true;
+ },
+
+ validate: function() {
+ let [status, buffer] = this._file.load_contents(null);
+ if (!status)
+ throw new Error(_("Failed to load file"));
+
+ let json = JSON.parse(buffer);
+ status = this._validateInternal(json);
+ if (!status)
+ throw new Error(_("Failed to validate GeoJSON"));
+
+ this._tileIndex = geojsonvt(json, { extent: 256,
+ maxZoom: 20 });
+ },
+
+ _renderTile: function(tile) {
+ let tileJSON = this._tileIndex.getTile(tile.zoom_level, tile.x, tile.y);
+
+ if (!tileJSON) {
+ tile.emit('render-complete', null, 0, false);
+ return;
+ }
+
+ let content = new Clutter.Canvas({ width: 256,
+ height: 256 });
+ tile.content = new Clutter.Actor({ width: 256,
+ height: 256,
+ content: content });
+
+ content.connect('draw', (function(canvas, cr) {
+ cr.setOperator(Cairo.Operator.CLEAR);
+ cr.paint();
+ cr.setOperator(Cairo.Operator.OVER);
+ cr.setSourceRGB(0, 0, 0);
+ cr.setLineWidth(1);
+
+ tileJSON.features.forEach(function(feature) {
+ if (feature.type === TileFeature.POINT)
+ return;
+
+ feature.geometry.forEach(function(geometry) {
+ let first = true;
+ cr.moveTo(0, 0);
+ geometry.forEach(function(coord) {
+ if (first) {
+ cr.moveTo(coord[0], coord[1]);
+ first = false;
+ } else {
+ cr.lineTo(coord[0], coord[1]);
+ }
+ });
+ });
+ if (feature.type === TileFeature.POLYGON)
+ cr.closePath();
+ cr.stroke();
+ });
+
+ tile.emit('render-complete', null, 0, false);
+ }).bind(this));
+
+ content.invalidate();
+ }
+});
diff --git a/src/org.gnome.Maps.src.gresource.xml b/src/org.gnome.Maps.src.gresource.xml
index e75f3fa..f7941ec 100644
--- a/src/org.gnome.Maps.src.gresource.xml
+++ b/src/org.gnome.Maps.src.gresource.xml
@@ -15,6 +15,7 @@
<file>foursquareGoaAuthorizer.js</file>
<file>geoclue.js</file>
<file>geocodeService.js</file>
+ <file>geoJSONSource.js</file>
<file>http.js</file>
<file>layersPopover.js</file>
<file>location.js</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]