Commits

Yohan Boniface committed e837c70

Working on KML import (#24)

Comments (0)

Files changed (5)

chickpea/tests/fixtures/test_upload_data.kml

+<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+  <Document>
+    <name>Paths</name>
+    <description>Examples of paths. Note that the tessellate tag is by default
+      set to 0. If you want to create tessellated lines, they must be authored
+      (or edited) directly in KML.</description>
+    <Style id="yellowLineGreenPoly">
+      <LineStyle>
+        <color>7f00ffff</color>
+        <width>4</width>
+      </LineStyle>
+      <PolyStyle>
+        <color>7f00ff00</color>
+      </PolyStyle>
+    </Style>
+    <Placemark>
+      <name>Absolute Extruded</name>
+      <description>Transparent green wall with yellow outlines</description>
+      <styleUrl>#yellowLineGreenPoly</styleUrl>
+      <LineString>
+        <extrude>1</extrude>
+        <tessellate>1</tessellate>
+        <altitudeMode>absolute</altitudeMode>
+        <coordinates>-112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357 -112.2564540158376,36.08395660588506,2357 -112.2580238976449,36.08511401044813,2357 -112.2595218489022,36.08584355239394,2357 -112.2608216347552,36.08612634548589,2357 -112.262073428656,36.08626019085147,2357 -112.2633204928495,36.08621519860091,2357 -112.2644963846444,36.08627897945274,2357 -112.2656969554589,36.08649599090644,2357</coordinates>
+      </LineString>
+    </Placemark>
+    <Placemark>
+      <name>Entity references example</name>
+      <description>
+            &lt;h1&gt;Entity references are hard to type!&lt;/h1&gt;
+            &lt;p&gt;&lt;font color="green"&gt;Text is 
+          &lt;i&gt;more readable&lt;/i&gt; 
+          and &lt;b&gt;easier to write&lt;/b&gt; 
+          when you can avoid using entity references.&lt;/font&gt;&lt;/p&gt;
+      </description>
+      <Point>
+        <coordinates>102.594411,14.998518</coordinates>
+      </Point>
+    </Placemark>
+  <Placemark>
+    <name>The Pentagon</name>
+    <Polygon>
+      <extrude>1</extrude>
+      <altitudeMode>relativeToGround</altitudeMode>
+      <outerBoundaryIs>
+        <LinearRing>
+          <coordinates>
+            -77.05788457660967,38.87253259892824,100 -77.05465973756702,38.87291016281703,100 -77.05315536854791,38.87053267794386,100 -77.05552622493516,38.868757801256,100 -77.05844056290393,38.86996206506943,100 -77.05788457660967,38.87253259892824,100</coordinates>
+        </LinearRing>
+      </outerBoundaryIs>
+      <innerBoundaryIs>
+        <LinearRing>
+          <coordinates>-77.05668055019126,38.87154239798456,100 -77.05542625960818,38.87167890344077,100 -77.05485125901024,38.87076535397792,100 -77.05577677433152,38.87008686581446,100 -77.05691162017543,38.87054446963351,100 -77.05668055019126,38.87154239798456,100</coordinates>
+        </LinearRing>
+      </innerBoundaryIs>
+    </Polygon>
+  </Placemark>
+  </Document>
+</kml>

leaflet_storage/forms.py

 from django.utils.translation import ugettext as _
 from django.template.defaultfilters import slugify
 
-from vectorformats.Formats import GeoJSON
+from VectorFormats.Formats import GeoJSON, KML
 
 from .models import Map, Category
 
             try:
                 features = geoj.decode(content)
             except:
-                raise forms.ValidationError('Invalid geojson')
+                raise forms.ValidationError('Invalid GeoJSON')
+        elif content_type == "application/vnd.google-earth.kml+xml":
+            kml = KML.KML()
+            try:
+                features = kml.decode(content)
+            except:
+                raise forms.ValidationError('Invalid KML')
         else:
             raise forms.ValidationError(_('Unsupported content_type: %s') % content_type)
         return features

leaflet_storage/tests/fixtures/test_upload_data.kml

+<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+  <Placemark>
+    <name>Simple point</name>
+    <description>Here is a simple description.</description>
+    <Point>
+      <coordinates>-122.0822035425683,37.42228990140251,0</coordinates>
+    </Point>
+  </Placemark>
+  <Placemark>
+    <name>Simple path</name>
+    <description>Simple description</description>
+    <LineString>
+      <coordinates>-112.2550785337791,36.07954952145647,2357 -112.2549277039738,36.08117083492122,2357 -112.2552505069063,36.08260761307279,2357</coordinates>
+    </LineString>
+  </Placemark>
+  <Placemark>
+    <name>Simple polygon</name>
+    <description>A description.</description>
+    <Polygon>
+      <outerBoundaryIs>
+        <LinearRing>
+          <coordinates>
+            -77.05788457660967,38.87253259892824,100 
+            -77.05465973756702,38.87291016281703,100 
+            -77.05315536854791,38.87053267794386,100 
+            -77.05788457660967,38.87253259892824,100 
+          </coordinates>
+        </LinearRing>
+      </outerBoundaryIs>
+    </Polygon>
+  </Placemark>
+</kml>

leaflet_storage/tests/views.py

         response = self.client.post(url, post_data)
         return response
 
-    def test_generic(self):
+    def test_GeoJSON_generic(self):
         # Contains tow Point, two Polygons and one Polyline
         response = self.process_file("test_upload_data.json")
         self.client.login(username=self.user.username, password="123123")
         self.assertEqual(marker.description, "")
         self.assertEqual(marker.color, None)
 
-    def test_empty_coordinates_should_not_be_imported(self):
+    def test_GeoJSON_empty_coordinates_should_not_be_imported(self):
         self.assertEqual(Marker.objects.filter(category=self.category).count(), 0)
         self.assertEqual(Polyline.objects.filter(category=self.category).count(), 0)
         self.assertEqual(Polygon.objects.filter(category=self.category).count(), 0)
         self.assertEqual(Polyline.objects.filter(category=self.category).count(), 0)
         self.assertEqual(Polygon.objects.filter(category=self.category).count(), 0)
 
-    def test_non_linear_ring_should_not_be_imported(self):
+    def test_GeoJSON_non_linear_ring_should_not_be_imported(self):
         response = self.process_file("test_upload_non_linear_ring.json")
         self.assertEqual(response.status_code, 200)
         self.assertEqual(Polygon.objects.filter(category=self.category).count(), 0)
 
-    def test_missing_name_should_not_stop_import(self):
+    def test_GeoJSON_missing_name_should_not_stop_import(self):
         # One feature is missing a name
         # We have to make sure that the other feature are imported
         self.assertEqual(Marker.objects.filter(category=self.category).count(), 0)
         self.assertEqual(Polygon.objects.filter(category=self.category).count(), 2)
         self.assertEqual(Polyline.objects.filter(category=self.category).count(), 1)
 
+    def test_KML_generic(self):
+        # Contains one Polyline
+        response = self.process_file("test_upload_data.kml")
+        self.client.login(username=self.user.username, password="123123")
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(Polyline.objects.filter(category=self.category).count(), 1)
+        self.assertEqual(Marker.objects.filter(category=self.category).count(), 1)
+        self.assertEqual(Polygon.objects.filter(category=self.category).count(), 1)
+
 
 class CategoryViews(BaseTest):
 

leaflet_storage/views.py

 from django.views.generic.edit import CreateView, UpdateView, FormView, DeleteView
 from django.contrib.auth.models import User
 
-from vectorformats.Formats import Django, GeoJSON
+from VectorFormats.Formats import Django, GeoJSON
 
 from .models import (Map, Marker, Category, Polyline, TileLayer,
                      MapToTileLayer, Polygon)
             'LineString': Polyline,
             'Polygon': Polygon
         }
-        FIELDS = ['name', 'description', 'color']
+        # Use a tuple to add more source possible
+        # first item is field name
+        FIELDS = [
+            ('name', 'title'),
+            'description',
+            'color'
+        ]
         features = form.cleaned_data.get('data_file', form.cleaned_data.get('data_url'))
         category = form.cleaned_data.get('category')
         counter = 0
             klass = FEATURE_TO_MODEL.get(feature.geometry['type'], None)
             if not klass:
                 continue  # TODO notify user
+            # Remove altitude, if there
             try:
-                latlng = GEOSGeometry(str(feature.geometry))
-            except:
+                if feature.geometry['type'] == "LineString":
+                    feature.geometry['coordinates'] = map(
+                        lambda x: x[:2],
+                        feature.geometry['coordinates']
+                    )
+                elif feature.geometry['type'] == "Point":
+                    feature.geometry['coordinates'] = feature.geometry['coordinates'][:2]
+                elif feature.geometry['type'] == "Polygon":
+                    feature.geometry['coordinates'] = map(
+                        lambda x: map(lambda y: y[:2], x),
+                        feature.geometry['coordinates']
+                    )
+            except Exception, e:
+                print e
+                continue
+            try:
+                latlng = GEOSGeometry(repr(feature.geometry))
+            except Exception, e:
+                print e
                 continue  # TODO notify user
             if latlng.empty:
                 continue  # TODO notify user
                 'category': category
             }
             for field in FIELDS:
-                if field in feature.properties:
-                    kwargs[field] = feature.properties[field]
+                if isinstance(field, tuple):
+                    name = field[0]
+                    candidates = field
+                else:
+                    name = field
+                    candidates = [field]
+                for candidate in candidates:
+                    if candidate in feature.properties:
+                        kwargs[name] = feature.properties[candidate]
+                        break
             try:
                 klass.objects.create(**kwargs)
             except DatabaseError: