Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

moved over content from geocamShare/apps/geocamLens

  • Loading branch information...
commit 9521b8afde9f1812a288c3370f5524823bf78c84 1 parent 569fed0
@trey0 trey0 authored
Showing with 4,600 additions and 58 deletions.
  1. +145 −0 geocamLens/SearchAbstract.py
  2. +18 −0 geocamLens/SearchSimple.py
  3. +105 −0 geocamLens/UploadClient.py
  4. +106 −0 geocamLens/ViewKml.py
  5. +245 −0 geocamLens/ViewLensAbstract.py
  6. +20 −0 geocamLens/ViewLensSimple.py
  7. +8 −14 geocamLens/__init__.py
  8. +6 −0 geocamLens/admin.py
  9. +225 −0 geocamLens/bin/simpleImport.py
  10. +4 −17 geocamLens/defaultSettings.py
  11. +40 −0 geocamLens/forms.py
  12. +34 −17 geocamLens/management/appCommands/prep.py
  13. +83 −0 geocamLens/media_src/icons/aerialhazard.svg
  14. +83 −0 geocamLens/media_src/icons/aerialignition.svg
  15. +103 −0 geocamLens/media_src/icons/base.svg
  16. +125 −0 geocamLens/media_src/icons/commandpost.svg
  17. +413 −0 geocamLens/media_src/icons/damaged.svg
  18. +90 −0 geocamLens/media_src/icons/downlink.svg
  19. +113 −0 geocamLens/media_src/icons/droppoint.svg
  20. +173 −0 geocamLens/media_src/icons/fireline.svg
  21. +181 −0 geocamLens/media_src/icons/fireorigin.svg
  22. +95 −0 geocamLens/media_src/icons/hazmat.svg
  23. +366 −0 geocamLens/media_src/icons/helispot.svg
  24. +211 −0 geocamLens/media_src/icons/hotspot.svg
  25. +253 −0 geocamLens/media_src/icons/relativelysafe.svg
  26. +103 −0 geocamLens/media_src/icons/repeater.svg
  27. +140 −0 geocamLens/media_src/icons/safetyzone.svg
  28. +138 −0 geocamLens/media_src/icons/spotfire.svg
  29. +103 −0 geocamLens/media_src/icons/stagingarea.svg
  30. +86 −0 geocamLens/media_src/icons/unsafe.svg
  31. +144 −0 geocamLens/media_src/icons/warning.svg
  32. +105 −0 geocamLens/media_src/icons/watersource.svg
  33. +420 −0 geocamLens/models.py
  34. BIN  geocamLens/static/geocamLens/icons/map/camera.png
  35. BIN  geocamLens/static/geocamLens/icons/map/cameraPoint.png
  36. 0  geocamLens/static/geocamLens/placeholder.txt
  37. +34 −0 geocamLens/templates/editImage.html
  38. +9 −0 geocamLens/templates/editImageWrapper.html
  39. 0  geocamLens/templates/geocamLens/placeholder.txt
  40. +13 −0 geocamLens/templates/upload.html
  41. 0  geocamLens/templatetags/__init__.py
  42. +22 −7 geocamLens/tests.py
  43. +38 −3 geocamLens/urls.py
View
145 geocamLens/SearchAbstract.py
@@ -0,0 +1,145 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+import re
+import sys
+
+from django.db.models import Q
+
+from geocamUtil import TimeUtil
+
+class BadQuery(Exception):
+ pass
+
+class SearchAbstract:
+ # override these settings in derived classes
+ getAllFeatures = None
+ fields = ()
+ fieldAliases = ()
+ timeField = 'timestamp'
+
+ def __init__(self):
+ self.flookup = dict([(name, name) for name in self.fields])
+ self.flookup.update(dict(self.fieldAliases))
+
+ def filterFieldBefore(self, query, clause, field, term, negated):
+ if negated:
+ raise BadQuery("Oops, can't use minus sign with BEFORE in clause '%s' of search '%s'" % (clause, query))
+ try:
+ # intervalStart=False to get end of specified interval
+ # (inclusive before)
+ utcDT = TimeUtil.stringToUtcDT(term, intervalStart=False)
+ except ValueError, msg:
+ raise BadQuery("Oops, %s in clause '%s' of search '%s'"
+ % (msg, clause, query))
+ return Q(**{self.timeField+'__lte': utcDT})
+
+ def filterFieldAfter(self, query, clause, field, term, negated):
+ if negated:
+ raise BadQuery("Oops, can't use minus sign with AFTER in clause '%s' of search '%s'" % (clause, query))
+ try:
+ # intervalStart=True to get start of specified interval
+ # (inclusive after)
+ utcDT = TimeUtil.stringToUtcDT(term, intervalStart=True)
+ except ValueError, msg:
+ raise BadQuery("Oops, %s in clause '%s' of search '%s'"
+ % (msg, clause, query))
+ return Q(**{self.timeField+'__gte': utcDT})
+
+ def filterFieldDefault(self, query, clause, field, term, negated):
+ if field == None:
+ fields = self.flookup.keys()
+ elif field not in self.flookup:
+ raise BadQuery("Oops, can't understand field name '%s' of search '%s'. Legal field names are: %s."
+ % (field, query, ', '.join(self.flookup.keys())))
+ else:
+ fields = [field]
+ if not re.search('^[\.\-\_a-zA-Z0-9]*$', term):
+ raise BadQuery("Oops, can't understand term '%s' in search '%s'. Terms must contain only letters, numbers, periods, hyphens, and underscores."
+ % (term, query))
+ if term == '':
+ raise BadQuery("Oops, empty search term in clause '%s' of search '%s'."
+ % (clause, query))
+ qfilter = Q()
+ for f in fields:
+ dbField = self.flookup[f]
+ qAdd = Q(**{dbField+'__icontains': term})
+ if negated:
+ qfilter = qfilter & ~qAdd
+ else:
+ qfilter = qfilter | qAdd
+ return qfilter
+
+ def filterField(self, query, clause, field, term, negated):
+ if field:
+ filterFuncName = 'filterField'+field.capitalize()
+ if hasattr(self, filterFuncName):
+ filterFunc = getattr(self, filterFuncName)
+ return filterFunc(query, clause, field, term, negated)
+ return self.filterFieldDefault(query, clause, field, term, negated)
+
+ def filterClause(self, query, clause):
+ if clause.startswith('-'):
+ negated = True
+ clause = clause[1:]
+ else:
+ negated = False
+ if ':' in clause:
+ field, term = clause.split(':', 1)
+ field = field.lower()
+ else:
+ field, term = None, clause
+ return self.filterField(query, clause, field, term, negated)
+
+ def queryTreeToString(self, queryTree):
+ return (' OR '
+ .join(['(%s)' % (' AND '
+ .join(['"%s"' % clause
+ for clause in term]))
+ for term in queryTree]))
+
+ def parseQuery(self, query):
+ if not re.search('^[\-\.\:\_a-zA-Z0-9 ]*$', query):
+ raise BadQuery("Oops, can't understand search '%s'. Searches must contain only letters, numbers, minus signs, colons, periods, underscores, and spaces."
+ % query)
+ queryClauses = query.split()
+ #print >>sys.stderr, 'queryClauses:', queryClauses
+
+ queryTree = [[]]
+ for clause in queryClauses:
+ if clause.lower() == 'or':
+ if queryTree[-1] == []:
+ raise BadQuery("Oops, 'or' must come between normal clauses in search '%s'" % query)
+ queryTree.append([])
+ elif clause.lower() == 'and':
+ if queryTree[-1] == []:
+ raise BadQuery("Oops, 'and' must come between normal clauses in search '%s'")
+ # ignore -- 'and' is default connective
+ else:
+ queryTree[-1].append(clause)
+ #print >>sys.stderr, 'queryTree:', queryTreeToString(queryTree)
+ return queryTree
+
+ def treeToFilter(self, query, queryTree):
+ queryFilter = Q()
+ for term in queryTree:
+ termFilter = Q()
+ for clause in term:
+ clauseFilter = self.filterClause(query, clause)
+ termFilter = termFilter & clauseFilter
+ queryFilter = queryFilter | termFilter
+ return queryFilter
+
+ def searchFeatures0(self, startSet, query):
+ queryTree = self.parseQuery(query)
+ queryFilter = self.treeToFilter(query, queryTree)
+ return startSet.filter(queryFilter)
+
+ def searchFeatures(self, startSet, query):
+ result = startSet
+ if query:
+ result = self.searchFeatures0(result, query)
+ return result.distinct().order_by('-'+self.timeField)
View
18 geocamLens/SearchSimple.py
@@ -0,0 +1,18 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+from geocamCore.models import PointFeature
+
+from geocamLens.SearchAbstract import SearchAbstract
+
+class SearchSimple(SearchAbstract):
+ def getAllFeatures(self):
+ return PointFeature.objects.filter(processed=True)
+
+ fields = ('name', 'user', 'notes', 'tags', 'uuid')
+ timeField = 'timestamp' # FIX: handle features with non-zero time extent
+ # pairs (user-facing-field-name, django-field-name)
+ fieldAliases = (('user', 'author__username'),)
View
105 geocamLens/UploadClient.py
@@ -0,0 +1,105 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+import os
+import urllib2
+import tempfile
+import re
+import base64
+
+import PIL.Image
+
+from geocamUtil import MimeMultipartFormData
+
+class UploadClient:
+ def __init__(self, url, userName='root', password=''):
+ self.url = url
+ self.userName = userName
+ self.password = password
+
+ self.imageName = None
+ self.im = None
+
+ def addAuthentication(self, headers, url):
+ '''update headers and return transformed url to support share
+ basic authentication.'''
+ if self.password:
+ # ensure https so we don't send password unencrypted
+ assert url.startswith('http')
+ url = re.sub(r'^http:', 'https:', url)
+ # new-style url which does not include userName
+ url = '%s/upload-m/' % url
+ # add pre-emptive basic authentication
+ auth = base64.encodestring('%s:%s' % (self.userName, self.password))[:-1]
+ headers['Authorization'] = 'Basic %s' % auth
+ else:
+ # no password means use old-style upload
+ url = '%s/upload/%s/' % (url, self.userName)
+ return url
+
+ def uploadImage(self, imageName, attributes, downsampleFactor=1):
+ if downsampleFactor != 1:
+ im = PIL.Image.open(imageName)
+ w, h = im.size
+ thRes = (w//downsampleFactor, h//downsampleFactor)
+ im.thumbnail(thRes, PIL.Image.ANTIALIAS)
+ fd, tmpName = tempfile.mkstemp('uploadImageThumb.jpg')
+ os.close(fd)
+ im.save(tmpName)
+ del im
+ imageData = file(tmpName, 'r').read()
+ os.unlink(tmpName)
+ else:
+ imageData = file(imageName, 'r').read()
+
+ #cookieProcessor = urllib2.HTTPCookieProcessor()
+ opener = urllib2.build_opener() # (cookieProcessor)
+ headers = {'User-Agent': 'GeoCam Upload Tester'}
+
+ multipart = MimeMultipartFormData.MimeMultipartFormData()
+ for k, v in attributes.iteritems():
+ multipart[k] = v
+ multipart.addFile(name='photo',
+ filename=os.path.basename(imageName),
+ data=imageData,
+ contentType='image/jpeg')
+
+ h2 = headers.copy()
+ h2.update(multipart.getHeaders())
+ url = self.addAuthentication(h2, self.url)
+ req = urllib2.Request(url=url,
+ data=multipart.getPostData(),
+ headers=h2)
+ resp = opener.open(req)
+ return resp
+
+ def uploadTrack(self, url, trackName, attributes=None):
+ trackData = file(trackName, 'r').read()
+
+ if attributes == None:
+ attributes = dict(trackUploadProtocolVersion='1.0')
+
+ #cookieProcessor = urllib2.HTTPCookieProcessor()
+ opener = urllib2.build_opener() # (cookieProcessor)
+ headers = {'User-Agent': 'GeoCam Upload Tester'}
+ url = '%s/track' % self.url
+
+ multipart = MimeMultipartFormData.MimeMultipartFormData()
+ for k, v in attributes.iteritems():
+ multipart[k] = v
+ multipart.addFile(name='gpxFile',
+ filename=os.path.basename(trackName),
+ data=trackData,
+ contentType='text/xml')
+
+ h2 = headers.copy()
+ h2.update(multipart.getHeaders())
+ url = self.addAuthentication(h2, url)
+ req = urllib2.Request(url=url,
+ data=multipart.getPostData(),
+ headers=h2)
+ resp = opener.open(req)
+ return resp
View
106 geocamLens/ViewKml.py
@@ -0,0 +1,106 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+import urllib
+import datetime
+import sys
+
+from geocamUtil import KmlUtil
+from geocamCore.models import Feature
+
+from geocamLens.models import GoogleEarthSession
+from geocamLens import settings
+
+class BogusRequest:
+ def build_absolute_uri(self, text):
+ return text
+
+class ViewKml(object):
+ def kmlGetStartSessionKml(self, request, sessionId):
+ quotedId = urllib.quote_plus(sessionId)
+ absUrl = request.build_absolute_uri('%skml/%s/initial.kml' % (settings.SCRIPT_NAME, quotedId))
+ if settings.GEOCAM_LENS_KML_FLY_TO_VIEW:
+ flyToView = '<flyToView>1</flyToView>'
+ else:
+ flyToView = ''
+ return ("""
+<NetworkLink>
+ <name>%(GEOCAM_CORE_SITE_TITLE)s</name>
+ <Link>
+ <href>%(absUrl)s</href>
+ </Link>
+ %(flyToView)s
+</NetworkLink>
+""" % dict(GEOCAM_CORE_SITE_TITLE=settings.GEOCAM_CORE_SITE_TITLE,
+ absUrl=absUrl,
+ flyToView=flyToView))
+
+ def kmlStartSession(self, request):
+ searchQuery = request.REQUEST.get('q', None)
+ sessionId = GoogleEarthSession.getSessionId(searchQuery)
+ print >>sys.stderr, "ViewKml: started session %s" % sessionId
+ return KmlUtil.wrapKmlDjango(self.kmlGetStartSessionKml(request, sessionId))
+
+ def kmlGetAllFeaturesFolder(self, request, searchQuery, newUtime):
+ features = self.search.searchFeatures(Feature.objects.all(), searchQuery)
+ if 0:
+ # FIX: update models so this filtering statement can work
+ features = features.filter(mtime__lte=newUtime,
+ deleted=False)
+ featuresKml = '\n'.join([f.getKml(request) for f in features])
+ return ("""
+<Folder id="allFeatures">
+ <name>All features</name>
+ %s
+</Folder>
+""" % featuresKml)
+
+ def kmlGetInitialKml(self, request, sessionId):
+ newUtime = datetime.datetime.now()
+ session, created = GoogleEarthSession.objects.get_or_create(sessionId=sessionId,
+ defaults=dict(utime=newUtime))
+ session.utime = newUtime
+ session.save()
+
+ allFeaturesFolder = self.kmlGetAllFeaturesFolder(request,
+ session.getSearchQuery(),
+ newUtime)
+ quotedId = urllib.quote_plus(sessionId)
+ updateUrl = request.build_absolute_uri('%skml/%s/update.kml' % (settings.SCRIPT_NAME, quotedId))
+ return ("""
+<Document id="allFeatures">
+ <name>%(GEOCAM_CORE_SITE_TITLE)s</name>
+
+ <NetworkLink>
+ <name>Update</name>
+ <Link>
+ <href>%(updateUrl)s</href>
+ <refreshMode>onInterval</refreshMode>
+ <refreshInterval>30</refreshInterval>
+ </Link>
+ </NetworkLink>
+
+ %(allFeaturesFolder)s
+
+</Document>
+""" % dict(GEOCAM_CORE_SITE_TITLE=settings.GEOCAM_CORE_SITE_TITLE,
+ updateUrl=updateUrl,
+ allFeaturesFolder=allFeaturesFolder))
+
+ def kmlGetUpdateKml(self, request, sessionId):
+ # FIX: implement me -- can use old version of geocam for reference
+ return ''
+
+ def kmlGetSessionResponse(self, request, quotedId, method):
+ sessionId = urllib.unquote_plus(quotedId)
+ #print 'sessionId:', sessionId
+ #print 'method:', method
+ if method == 'initial':
+ return KmlUtil.wrapKmlDjango(self.kmlGetInitialKml(request, sessionId))
+ elif method == 'update':
+ return KmlUtil.wrapKmlDjango(self.kmlGetUpdateKml(request, sessionId))
+ else:
+ raise Exception('method must be "initial" or "update"')
View
245 geocamLens/ViewLensAbstract.py
@@ -0,0 +1,245 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+# Create your views here.
+
+import math
+import sys
+import datetime
+import os
+import shutil
+import urllib
+import tempfile
+import shutil
+
+import PIL.Image
+import tagging
+from django.http import HttpResponse
+from django.shortcuts import render_to_response
+from django.utils.safestring import mark_safe
+from django.template import RequestContext
+from django.contrib.auth.models import User
+
+from geocamUtil import anyjson as json
+from geocamUtil.icons import cacheIcons
+from geocamUtil.middleware.SecurityMiddleware import requestIsSecure
+from geocamUtil.models.UuidField import makeUuid
+from geocamUtil.FileUtil import mkdirP
+
+from geocamLens.models import Image
+from geocamLens.forms import UploadImageForm, EditImageForm
+from geocamLens.ViewKml import ViewKml
+from geocamLens.SearchAbstract import BadQuery
+from geocamLens import settings
+
+cacheIcons(os.path.join(settings.MEDIA_ROOT, 'geocamLens', 'icons', 'map'))
+cacheIcons(os.path.join(settings.MEDIA_ROOT, 'geocamLens', 'icons', 'mapr'))
+
+class ViewLensAbstract(ViewKml):
+ # override in derived classes
+ search = None
+ defaultImageModel = None
+
+ def getMatchingFeaturesForQuery(self, query):
+ allFeatures = self.search.getAllFeatures()
+ features = self.search.searchFeatures(allFeatures, query)
+ return features
+
+ def getMatchingFeatures(self, request):
+ query = request.REQUEST.get('q', '')
+ return self.getMatchingFeaturesForQuery(query)
+
+ def dumps(self, obj):
+ if 1:
+ return json.dumps(obj, indent=4, sort_keys=True) # pretty print for debugging
+ else:
+ return json.dumps(obj, separators=(',',':')) # compact
+
+ def getFeaturesGeoJson(self, request):
+ try:
+ matches = self.getMatchingFeatures(request)
+ errorMessage = None
+ except BadQuery, e:
+ errorMessage = e.message
+
+ if errorMessage:
+ response = {'error': {'code': -32099,
+ 'message': errorMessage}}
+ else:
+ gjFeatures = [f.getGeoJson() for f in matches]
+ featureCollection = dict(type='FeatureCollection',
+ crs=dict(type='name',
+ properties=dict(name='urn:ogc:def:crs:OGC:1.3:CRS84')),
+ features=gjFeatures)
+ response = dict(result=featureCollection)
+ return self.dumps(response)
+
+ def featuresJson(self, request):
+ return HttpResponse(self.getFeaturesGeoJson(request),
+ mimetype='application/json')
+
+ def featuresJsonJs(self, request):
+ response = self.getFeaturesGeoJson(request)
+ return HttpResponse('geocamAware.handleNewFeatures(%s);\n' % response,
+ mimetype='text/javascript')
+
+ def galleryDebug(self, request):
+ return HttpResponse('<body><pre>%s</pre></body>' % self.getFeaturesGeoJson(request))
+
+ def getExportSettings(self):
+ exportedVars = ['SCRIPT_NAME',
+ 'MEDIA_URL',
+ 'DATA_URL',
+ 'GEOCAM_AWARE_GALLERY_PAGE_COLS',
+ 'GEOCAM_AWARE_GALLERY_PAGE_ROWS',
+ 'GEOCAM_CORE_GALLERY_THUMB_SIZE',
+ 'GEOCAM_CORE_DESC_THUMB_SIZE',
+ 'GEOCAM_AWARE_MAP_BACKEND',
+ 'GEOCAM_AWARE_USE_MARKER_CLUSTERING',
+ 'GEOCAM_AWARE_USE_TRACKING']
+ exportDict = dict(((f, getattr(settings, f))
+ for f in exportedVars))
+ return json.dumps(exportDict)
+
+ def editImage0(self, request, uuid, template):
+ img = self.defaultImageModel.objects.get(uuid = uuid)
+ ajax = request.POST.has_key('ajax')
+ if request.method == 'POST':
+ form = EditImageForm(request.POST, instance=img)
+ if form.is_valid():
+ # FIX: update map, etc!
+ updatedObject = form.save()
+ if ajax:
+ return HttpResponse(json.dumps({'result': updatedObject.getGeoJson()}),
+ mimetype='application/json')
+ else:
+ if ajax:
+ return HttpResponse(json.dumps({'error': {'code': -32099,
+ 'message': 'invalid value in form field',
+ 'data': form._get_errors()}
+ }),
+ mimetype='application/json')
+ else:
+ form = EditImageForm(instance=img)
+ return (render_to_response
+ (template,
+ dict(img=img,
+ form=form),
+ context_instance = RequestContext(request)))
+
+ def editImage(self, request, uuid):
+ return self.editImage0(request, uuid, 'editImage.html')
+
+ def editImageWrapper(self, request, uuid):
+ return self.editImage0(request, uuid, 'editImageWrapper.html')
+
+ def uploadImageAuth(self, request):
+ return self.uploadImage(request, request.user.username)
+
+ def uploadImage(self, request, userName):
+ author = User.objects.get(username=userName)
+ if request.method == 'POST':
+ print >>sys.stderr, 'upload image start'
+ form = UploadImageForm(request.POST, request.FILES)
+ print >>sys.stderr, 'FILES:', request.FILES.keys()
+ if form.is_valid():
+ incoming = request.FILES['photo']
+
+ # store image data in temp file
+ fd, tempStorePath = tempfile.mkstemp('-uploadImage.jpg')
+ os.close(fd)
+ storeFile = file(tempStorePath, 'wb')
+ for chunk in incoming.chunks():
+ storeFile.write(chunk)
+ storeFile.close()
+ print >>sys.stderr, 'upload: saved image data to temp file:', tempStorePath
+
+ # create image db record
+ uuid = form.cleaned_data.setdefault('uuid', makeUuid())
+ form.cleaned_data['name'] = incoming.name
+ form.cleaned_data['author'] = author
+ uuidMatches = self.defaultImageModel.objects.filter(uuid=uuid)
+ sameUuid = (uuidMatches.count() > 0)
+ if sameUuid:
+ # if the incoming uuid matches an existing uuid, this is
+ # either (1) a duplicate upload of the same image or (2)
+ # the next higher resolution level in an incremental
+ # upload.
+ img = uuidMatches.get()
+ print >>sys.stderr, 'upload: photo %s with same uuid %s posted' % (img.name, img.uuid)
+ newVersion = img.version + 1
+ else:
+ # create Image db record
+ img = self.defaultImageModel()
+ img.readImportVals(storePath=tempStorePath,
+ uploadImageFormData=form.cleaned_data)
+
+ # set version
+ newVersion = 0
+
+ # check the new image file on disk to get the dimensions
+ im = PIL.Image.open(tempStorePath, 'r')
+ newRes = im.size
+ del im
+
+ if sameUuid:
+ oldRes = (img.widthPixels, img.heightPixels)
+ if newRes > oldRes:
+ print >>sys.stderr, 'upload: resolution increased from %d to %d' % (oldRes[0], newRes[0])
+ img.widthPixels, img.heightPixels = newRes
+ img.processed = False
+ else:
+ print >>sys.stderr, 'upload: ignoring dupe, but telling the client it was received so it stops trying'
+ else:
+ img.widthPixels, img.heightPixels = newRes
+
+ if not img.processed:
+ # generate thumbnails and any other processing
+ # (better to do this part in the background, but we
+ # don't have that set up yet)
+ img.version = newVersion
+ # make sure the image gets an id if it doesn't already have one --
+ # the id will be used in process() to calculate the storage path
+ img.save()
+ img.process(importFile=tempStorePath)
+ img.save()
+
+ # after import by process(), can delete redundant temp copy
+ os.unlink(tempStorePath)
+
+ print >>sys.stderr, 'upload image end'
+
+ # swfupload requires non-empty response text.
+ # also added a text pattern (in html comment) for clients to check against to make sure
+ # photo has actually arrived in share. we also put a matching line in the error log so we
+ # never again run into the issue that the phone thinks it successfully uploaded but there
+ # is no record of the http post on the server.
+ print >>sys.stderr, 'GEOCAM_SHARE_POSTED %s' % img.name
+ return HttpResponse('file posted <!--\nGEOCAM_SHARE_POSTED %s\n-->' % img.name)
+
+ else:
+ print >>sys.stderr, "form is invalid"
+ print >>sys.stderr, "form errors: ", form._errors
+ userAgent = request.META.get('HTTP_USER_AGENT', '')
+ # swfupload user can't see errors in form response, best return an error code
+ if 'Flash' in userAgent:
+ return http.HttpResponseBadRequest('<h1>400 Bad Request</h1>')
+ else:
+ form = UploadImageForm()
+ #print 'form:', form
+ resp = render_to_response('upload.html',
+ dict(form=form,
+ author=author,
+ ),
+ context_instance=RequestContext(request))
+ print >>sys.stderr, 'upload image end'
+ return resp
+
+ def viewPhoto(self, request, id):
+ # this is not very efficient
+ img = Image.objects.get(id=id)
+ imgData = file(img.getImagePath(), 'r').read()
+ return HttpResponse(imgData, mimetype='image/jpeg')
View
20 geocamLens/ViewLensSimple.py
@@ -0,0 +1,20 @@
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+import os
+
+from geocamUtil.icons import cacheIcons
+
+from geocamLens.ViewLensAbstract import ViewLensAbstract
+from geocamLens.models import Photo
+from geocamLens.SearchSimple import SearchSimple
+from geocamLens import settings
+
+class ViewLensSimple(ViewLensAbstract):
+ search = SearchSimple()
+ defaultImageModel = Photo
+
+viewSingleton = ViewLensSimple()
View
22 geocamLens/__init__.py
@@ -5,9 +5,15 @@
# __END_LICENSE__
"""
-geocamLensWeb
+geocamLens
"""
+import django.conf
+
+from geocamUtil.MultiSettings import MultiSettings
+from geocamCore import defaultSettings as coreDefaults
+from geocamLens import defaultSettings as lensDefaults
+
__version_info__ = {
'major': 0,
'minor': 1,
@@ -30,16 +36,4 @@ def get_version():
__version__ = get_version()
-MultiSettings = None
-try:
- from geocamUtil.MultiSettings import MultiSettings
-except ImportError:
- import sys
- print >>sys.stderr, "warning: geocamUtil not installed, can't load defaultSettings.py"
-
-if MultiSettings:
- import django.conf
- import defaultSettings
- settings = MultiSettings(django.conf.settings, defaultSettings)
-else:
- from django.conf import settings
+settings = MultiSettings(django.conf.settings, coreDefaults, lensDefaults)
View
6 geocamLens/admin.py
@@ -5,3 +5,9 @@
# __END_LICENSE__
from django.contrib import admin
+
+from geocamLens.models import *
+
+admin.site.register(Snapshot)
+admin.site.register(GoogleEarthSession)
+admin.site.register(Photo)
View
225 geocamLens/bin/simpleImport.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+# __BEGIN_LICENSE__
+# Copyright (C) 2008-2010 United States Government as represented by
+# the Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+# __END_LICENSE__
+
+"""Simplest possible import to get some data to work with. This code
+will not be used in production."""
+
+import sys
+import os
+import datetime
+import glob
+import csv
+import re
+import uuid
+import getpass
+import stat
+
+import PIL
+import pytz
+import tagging
+from django.contrib.auth.models import User
+from django.core.exceptions import ObjectDoesNotExist
+
+from geocamUtil import TimeUtil
+from geocamUtil.FileUtil import mkdirP
+from geocamCore.models import Feature, Folder
+
+from geocamLens.UploadClient import UploadClient
+from geocamLens.models import Photo
+from geocamLens import settings
+
+def checkMissing(val):
+ if val == -999:
+ return None
+ else:
+ return val
+
+def importImageDirect(imagePath, attributes):
+ try:
+ author = User.objects.get(username=attributes['userName'])
+ except ObjectDoesNotExist:
+ print 'ERROR: No Django user "%s"; specify an existing user with the --user option, or create the user' % attributes['userName']
+ sys.exit(1)
+ attributes['author'] = author
+
+ name = os.path.basename(imagePath)
+ attributes['name'] = name
+
+ matchingPhotos = Photo.objects.filter(name=name,
+ author=author)
+
+ if matchingPhotos:
+ print 'skipping already imported', unicode(matchingPhotos[0])
+ else:
+ photo = Photo()
+ photo.readImportVals(storePath=imagePath, uploadImageFormData=attributes)
+ photo.save()
+ photo.process(importFile=imagePath)
+ photo.save()
+ print 'processed', unicode(photo)
+
+def getBogusUuid(name, userName, timeStr):
+ return str(uuid.uuid3(uuid.NAMESPACE_DNS, '%s-%s-%s' % (name, userName, timeStr)))
+
+def importDir(opts, dir, uploadClient):
+ dir = os.path.realpath(dir)
+
+ tzFile = '%s/timezone.txt' % dir
+ if os.path.exists(tzFile):
+ timeZone = file(tzFile, 'r').read().strip()
+ else:
+ timeZone = 'US/Pacific' # default
+
+ folderName = os.path.basename(dir)
+ if not opts.upload:
+ folder, created = Folder.objects.get_or_create(name=folderName,
+ defaults=dict(timeZone=timeZone))
+
+ photosToUpload = []
+
+ csvFiles = glob.glob('%s/*.csv' % dir)
+ if csvFiles:
+ csvName = csvFiles[0]
+ reader = csv.reader(file(csvName, 'r'))
+ firstLine = True
+ i = 0
+ for row in reader:
+ if firstLine:
+ firstLine = False
+ continue
+ if opts.number != 0 and i >= opts.number:
+ break
+ allText = ' '.join(row)
+ if opts.match and not re.search(opts.match, allText):
+ continue
+ latStr, lonStr, compassStr, timeStr, name, notes, tagsStr, creatorName = row
+ tags = [t.strip() for t in tagsStr.split(',')]
+ tags = [t for t in tags if t != 'default']
+ tags.append(creatorName)
+ tagsDb = ', '.join(tags)
+ lat, lon, compass = float(latStr), float(lonStr), float(compassStr)
+ if lat == -999:
+ lat, lon = None, None
+ imagePath = os.path.join(dir, 'photos', name)
+
+ # make up a consistent bogus uuid field so we can test incremental upload.
+ # real clients should always make a stronger uuid to avoid collisions!
+ bogusUuid = getBogusUuid(name, opts.user, timeStr)
+
+ # map to field names in upload form
+ attributes = dict(name=name,
+ userName=opts.user,
+ cameraTime=timeStr,
+ latitude=lat,
+ longitude=lon,
+ altitude=None,
+ altitudeRef=None,
+ roll=None,
+ pitch=None,
+ yaw=compass,
+ notes=notes,
+ tags=tagsDb,
+ uuid=bogusUuid,
+ folder=folderName)
+
+ photosToUpload.append(dict(imagePath=imagePath,
+ attributes=attributes))
+ i += 1
+ else:
+ images = []
+ for ext in ('.jpg', '.jpeg', '.JPG'):
+ images += glob.glob('%s/*%s' % (dir, ext))
+ if images:
+ if opts.match:
+ print >>sys.stderr, "warning: can't import dir %s -- can't match tags unless we have a .csv" % dir
+ images = []
+ if opts.number != 0:
+ images = images[:opts.number]
+ for img in images:
+ name = os.path.basename(img)
+ timeStr = os.stat(img)[stat.ST_MTIME]
+ bogusUuid = getBogusUuid(name, opts.user, timeStr)
+
+ attributes = dict(name=name,
+ userName=opts.user,
+ uuid=bogusUuid,
+ folder=os.path.basename(os.path.dirname(img)))
+
+ photosToUpload.append(dict(imagePath=img,
+ attributes=attributes))
+ else:
+ print >>sys.stderr, "warning: can't import dir %s -- no *.csv, no images" % dir
+
+ for p in photosToUpload:
+ if uploadClient:
+ print 'uploading', os.path.basename(p['imagePath'])
+ uploadClient.uploadImage(p['imagePath'], p['attributes'], downsampleFactor=int(opts.downsample))
+ else:
+ importImageDirect(p['imagePath'], p['attributes'])
+
+def doit(opts, importDirs):
+ if opts.downsample != '1':
+ opts.upload = True
+ if opts.password:
+ opts.secure = True
+ if opts.secure:
+ opts.upload = True
+ assert opts.url
+ if opts.password == None:
+ opts.password = getpass.getpass('password for %s at %s: ' % (opts.user, opts.url))
+ if opts.upload:
+ opts.url = opts.url.rstrip('/')
+ uploadClient = UploadClient(opts.url, opts.user, opts.password)
+ else:
+ uploadClient = None
+ if opts.clean:
+ print 'cleaning'
+ features = Feature.objects.all()
+ for f in features:
+ f.deleteFiles()
+ f.delete()
+ for dir in importDirs:
+ importDir(opts, dir, uploadClient)
+
+def main():
+ import optparse
+ parser = optparse.OptionParser('usage: %prog <dir1> [dir2 ...]')
+ parser.add_option('-c', '--clean',
+ action='store_true', default=False,
+ help='Clean database before import')
+ parser.add_option('-m', '--match',
+ default=None,
+ help='Import only photos with tags matching pattern')
+ parser.add_option('-n', '--number',
+ default=0,
+ help='Limit number of photos to import')
+ parser.add_option('-u', '--upload',
+ action='store_true', default=False,
+ help='Simulate client HTTP upload rather than directly connecting to db')
+ parser.add_option('-s', '--secure',
+ action='store_true', default=False,
+ help='Use share v2 secure upload. Implies -u.')
+ parser.add_option('--url',
+ default='http://localhost:8000' + settings.SCRIPT_NAME,
+ help='Server url for client upload [%default]')
+ parser.add_option('--user',
+ default=getpass.getuser(),
+ help='Author of imported photos [%default]')
+ parser.add_option('-p', '--password',
+ default=None,
+ help='Password to use for upload authentication. Implies -s.')
+ parser.add_option('-d', '--downsample',
+ default='1',
+ help='Downsample images by specified factor before upload. Implies -u.')
+ opts, args = parser.parse_args()
+ if not args:
+ print >>sys.stderr, 'warning: no import dirs specified, not importing anything'
+ importDirs = args
+ doit(opts, importDirs)
+
+if __name__ == '__main__':
+ main()
View
21 geocamLens/defaultSettings.py
@@ -4,23 +4,10 @@
# All Rights Reserved.
# __END_LICENSE__
-"""
-This app may define some new parameters that can be modified in the
-Django settings module. Let's say one such parameter is FOO. The
-default value for FOO is defined in this file, like this:
+GEOCAM_LENS_DEFAULT_ICON = 'camera'
- FOO = 'my default value'
+GEOCAM_LENS_KML_FLY_TO_VIEW = True
-If the admin for the site doesn't like the default value, they can
-override it in the site-level settings module, like this:
+GEOCAM_LENS_RENDER_SVG_ICONS = True
- FOO = 'a better value'
-
-Other modules can access the value of FOO like this:
-
- from geocamLens import settings
- print settings.FOO
-
-Don't try to get the value of FOO from django.conf.settings. That
-settings object will not know about the default value!
-"""
+GEOCAM_LENS_VIEW_MODULE = 'geocamLens.ViewLensSimple'
View
40 geocamLens/forms.py
@@ -4,4 +4,44 @@
# All Rights Reserved.
# __END_LICENSE__
+import datetime
+
+from django.core.exceptions import ValidationError
from django import forms
+from django.contrib.auth.models import User
+
+from geocamUtil.forms.UuidField import UuidField
+
+from geocamLens.models import Photo
+
+# the field names in this form are currently retained for backward compatibility with old versions
+# of GeoCam Mobile
+class UploadImageForm(forms.Form):
+ photo = forms.FileField(required=True)
+ cameraTime = forms.CharField(required=False)
+ longitude = forms.FloatField(required=False)
+ latitude = forms.FloatField(required=False)
+ roll = forms.FloatField(required=False)
+ pitch = forms.FloatField(required=False)
+ yaw = forms.FloatField(required=False)
+ yawRef = forms.CharField(max_length=1, required=False)
+ altitude = forms.FloatField(required=False)
+ altitudeRef = forms.CharField(max_length=1, required=False)
+ tags = forms.CharField(max_length=256, required=False)
+ notes = forms.CharField(max_length=2048, required=False)
+ importFileMtimeUtc = forms.DateTimeField(required=False, initial=datetime.datetime.utcfromtimestamp(0))
+ uuid = UuidField(required=False)
+ folder = forms.CharField(max_length=32, required=False)
+
+class EditImageForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ super(EditImageForm, self).__init__(*args, **kwargs)
+
+ # change default widgets
+ self.fields['notes'].widget = forms.TextInput(attrs={'size': '50'})
+ self.fields['tags'].widget = forms.TextInput(attrs={'size': '50'})
+
+ class Meta:
+ model = Photo
+ fields = ('notes', 'tags', 'latitude', 'longitude', 'altitude', 'altitudeRef', 'yaw', 'yawRef', 'icon')
+
View
51 geocamLens/management/appCommands/prep.py
@@ -4,27 +4,44 @@
# All Rights Reserved.
# __END_LICENSE__
-"""
-This is a place to put any prep code you need to run before your app
-is ready.
-
-For example, you might need to render some icons. The convention for
-that is to put the source data in your app's media_src directory and
-render the icons into your app's build/media directory (outside version
-control).
-
-How this script gets run: when the site admin runs "./manage.py prep",
-one of the steps is "prepapps", which calls
-management/appCommands/prep.py command for each app (if it exists).
-"""
+import logging
+import os
+from glob import glob
from django.core.management.base import NoArgsCommand
-from geocamUtil.management import commandUtil
+from geocamUtil.Builder import Builder
+from geocamUtil.icons import svg, rotate
+from geocamUtil.Installer import Installer
+
+from geocamLens import settings
class Command(NoArgsCommand):
- help = 'Prep geocamLens'
+ help = 'Prep geocamLens app'
def handle_noargs(self, **options):
- # put your code here
- pass
+ up = os.path.dirname
+ appDir = up(up(up(os.path.abspath(__file__))))
+ print 'appDir:', appDir
+ builder = Builder()
+
+ # render svg to png
+ if settings.GEOCAM_LENS_RENDER_SVG_ICONS:
+ svgGlob = '%s/media_src/icons/*.svg' % appDir
+ svgOutput = '%s/build/media/geocamLens/icons/map/' % appDir
+ logging.debug('svgIcons %s %s' % (svgGlob, svgOutput))
+ for imPath in glob(svgGlob):
+ svg.buildIcon(builder, imPath, outputDir=svgOutput)
+
+ # link static stuff into build/media
+ inst = Installer(builder)
+ inst.installRecurseGlob('%s/static/*' % appDir,
+ '%s/build/media' % appDir)
+
+ # rotate pngs
+ rotGlob = '%s/build/media/geocamLens/icons/map/*Point.png' % appDir
+ rotOutput = '%s/build/media/geocamLens/icons/mapr' % appDir
+ logging.debug('rotateIcons %s %s' % (rotGlob, rotOutput))
+ for imPath in glob(rotGlob):
+ rotate.buildAllDirections(builder, imPath, outputDir=rotOutput)
+
View
83 geocamLens/media_src/icons/aerialhazard.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="aerialhazard.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs9131">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 256 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="512 : 256 : 1"
+ inkscape:persp3d-origin="256 : 170.66667 : 1"
+ id="perspective9" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.25"
+ inkscape:cx="-1045.0921"
+ inkscape:cy="275.12506"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="1280"
+ inkscape:window-height="725"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:snap-global="false"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2480"
+ visible="true"
+ enabled="true"
+ dotted="false"
+ spacingx="2px"
+ spacingy="2px"
+ empspacing="8" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <path
+ style="fill:#a900e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:round;marker-mid:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
+ d="M 256,11.515656 C 223.79564,150.01575 158.98618,285.70268 50.595229,380.47097 C 42.502128,389.04074 8.8688742,408.95219 38.023293,395.32132 C 101.30175,368.55396 166.84282,341.17831 217.77793,293.73507 C 232.45122,285.25423 230.69338,258.91207 250.81877,264.86282 C 258.63111,266.33435 271.27114,261.76779 275.79923,267.34698 C 291.54555,300.75876 325.9056,319.97829 355.91083,339.0023 C 398.12046,363.56337 443.00409,383.16109 488.37903,401.04773 C 409.07,344.39293 352.21645,261.27933 311.08298,174.03148 C 287.005,122.01958 268.9294,67.322763 256,11.515656 z M 257.72934,398.4537 C 220.58668,396.76871 193.67755,441.91543 212.87422,473.77952 C 228.99037,506.9195 280.80176,509.43284 300.02818,477.98579 C 323.28704,445.64026 297.58716,397.28868 257.72934,398.4537 z"
+ id="path6136" />
+ </g>
+</svg>
View
83 geocamLens/media_src/icons/aerialignition.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="aerialignition.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs9131">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 256 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="512 : 256 : 1"
+ inkscape:persp3d-origin="256 : 170.66667 : 1"
+ id="perspective9" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.49497475"
+ inkscape:cx="390.89008"
+ inkscape:cy="253.15487"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="780"
+ inkscape:window-height="642"
+ inkscape:window-x="358"
+ inkscape:window-y="43"
+ inkscape:snap-global="false"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2480"
+ visible="true"
+ enabled="true"
+ dotted="false"
+ spacingx="2px"
+ spacingy="2px"
+ empspacing="8" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <path
+ style="opacity:1;fill:#ff0000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+ d="M 86.868423,17.640804 C 47.044158,16.209481 13.944046,58.394694 25.348596,96.718926 C 34.239554,136.59976 85.495107,157.48878 119.67579,135.01301 C 154.19182,115.70289 160.55458,63.12752 131.60473,36.167774 C 119.92266,24.379946 103.46878,17.548496 86.868423,17.640804 z M 256.4549,117.32265 C 216.6071,115.87263 183.84934,158.1925 195.18344,196.41822 C 203.68521,235.15921 252.37406,256.75085 286.61301,236.42212 C 323.05844,218.193 331.11665,163.63471 301.19121,135.84966 C 289.50913,124.06183 273.05527,117.23039 256.4549,117.32265 z M 86.868423,192.28339 C 47.044134,190.85204 13.944039,233.03728 25.348596,271.36152 C 34.239558,311.24235 85.495118,332.13138 119.67579,309.65559 C 154.19181,290.34545 160.55458,237.77011 131.60473,210.81036 C 119.92266,199.02253 103.46878,192.19108 86.868423,192.28339 z M 427.10461,193.6125 C 387.26605,192.1626 354.5022,234.47005 365.83315,272.69062 C 374.39988,311.44033 423.1199,332.62008 457.32437,312.5078 C 492.8619,294.97591 501.94704,242.62723 474.0808,214.33647 C 462.22219,201.2473 444.77766,193.50965 427.10461,193.6125 z M 256.4549,291.43362 C 216.61631,289.98369 183.85246,332.29116 195.18344,370.51174 C 203.74081,409.22614 252.43192,430.45393 286.61301,410.32891 C 322.02519,392.69965 330.91685,340.48168 303.28574,312.15756 C 291.49572,299.05638 274.08756,291.32464 256.4549,291.43362 z M 86.868423,369.84998 C 47.044155,368.41867 13.944044,410.60388 25.348596,448.92812 C 34.239551,488.80896 85.49511,509.69795 119.67579,487.22219 C 154.1918,467.91208 160.55459,415.33672 131.60473,388.377 C 119.92265,376.58917 103.46876,369.75775 86.868423,369.84998 z"
+ id="path3301" />
+ </g>
+</svg>
View
103 geocamLens/media_src/icons/base.svg
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="base.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs9131">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 256 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="512 : 256 : 1"
+ inkscape:persp3d-origin="256 : 170.66667 : 1"
+ id="perspective17" />
+ <filter
+ inkscape:collect="always"
+ id="filter3438">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="2.7669415"
+ id="feGaussianBlur3440" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="77.08529"
+ inkscape:cy="283.89466"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="1123"
+ inkscape:window-height="721"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:snap-global="false"
+ showborder="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid9160"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#4473dc;fill-opacity:1;stroke:#000000;stroke-width:20.00000191;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 256.41681,12.778719 C 152.7812,11.185274 53.510905,83.635784 24.007483,183.10444 C -5.7367286,273.48349 24.778376,379.23819 97.47159,440.43472 C 170.05165,504.42241 281.89082,517.67526 367.32203,472.15187 C 451.0951,430.32916 504.80652,336.71277 498.89021,243.30241 C 494.89746,146.09599 426.5732,55.53623 333.88665,25.631559 C 308.97503,17.232729 282.7121,12.819069 256.41681,12.778719 z M 255.71144,43.910912 C 348.98105,42.121842 437.75152,109.75202 460.65016,200.22447 C 485.97914,288.03862 446.34925,388.95567 368.04044,436.08143 C 290.25889,486.08035 180.40757,476.34373 113.05661,412.79767 C 41.005593,349.47194 22.68493,235.36057 70.89251,152.51504 C 107.10001,86.432209 180.34645,43.34096 255.71144,43.910912 z"
+ id="path3202" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 477.21701,256 C 478.96779,354.26498 406.70137,447.20343 311.26456,470.24863 C 221.22029,494.87849 118.81816,454.51556 69.649422,375.20586 C 15.827139,294.132 25.584222,178.1765 92.273216,107.28368 C 155.27136,35.598378 266.54776,13.933278 351.87927,56.608129 C 427.33566,92.201238 478.1377,172.44024 477.21701,256 z"
+ id="path2435" />
+ <path
+ style="fill:#4473dc;fill-opacity:1;stroke:#4473dc;stroke-width:20;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 256.3902,28.30271 C 157.6835,26.719546 63.424468,97.05318 37.414522,192.39362 C 8.8936992,285.03691 48.312814,392.80454 129.67753,445.4442 C 208.90411,499.82103 322.32322,495.49095 396.94343,434.74686 C 475.15104,375.22154 505.40056,262.32852 467.43361,171.67426 C 434.45183,86.342894 347.71085,27.685346 256.3902,28.30271 z M 255.72986,57.447838 C 344.71617,55.674267 429.09055,121.56606 448.78085,208.40803 C 471.03241,292.80944 429.44208,388.28931 352.51535,429.51703 C 275.87878,473.73279 170.98675,457.75674 111.39325,392.19577 C 47.469523,326.73839 39.493526,216.11598 93.371346,142.16562 C 129.56312,89.677229 191.98156,57.152202 255.72986,57.447838 z"
+ id="path3253" />
+ <path
+ style="font-size:314.04598999px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;opacity:0.3415638;fill:#4473dc;fill-opacity:1;stroke:#000000;stroke-width:10.39627647px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3438);font-family:Sans;-inkscape-font-specification:Sans"
+ d="M 222.78381,260.23758 C 222.78381,288.19708 222.78381,316.15659 222.78381,344.11609 C 248.06597,343.06543 274.00217,347.01792 298.61891,340.10284 C 331.48546,328.48735 329.33567,269.99667 294.20311,262.80616 C 270.83095,258.22799 246.51202,260.99917 222.78381,260.23758 z M 222.78381,166.08513 C 222.78381,189.08654 222.78381,212.08796 222.78381,235.08937 C 247.23209,233.93698 272.57787,238.35204 296.11992,230.34541 C 322.94859,219.14352 318.07241,172.21998 288.41442,168.23208 C 266.8321,164.43026 244.62762,166.70908 222.78381,166.08513 z M 191.80858,140.63023 C 226.26768,141.41114 260.94446,138.74031 295.21421,142.70052 C 322.22043,145.8184 346.68505,168.85027 344.69132,197.21371 C 346.68718,220.53404 330.29716,244.25303 306.04894,246.13006 C 340.19987,250.87976 360.95659,287.59982 352.89504,319.93587 C 347.57062,354.3118 310.12697,370.29148 278.74252,369.50569 C 249.76541,369.65392 220.78643,369.53049 191.80858,369.57098 C 191.80858,293.2574 191.80858,216.94382 191.80858,140.63023 z"
+ id="text3362"
+ transform="matrix(0.9571241,0,0,0.963885,-9.5087661,17.47903)" />
+ <path
+ style="font-size:301.64074707px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#406dd1;fill-opacity:1;stroke:#406dd1;stroke-width:15;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;font-family:Sans;-inkscape-font-specification:Sans"
+ d="M 216.07226,261.45743 C 216.07226,288.40717 216.07226,315.35692 216.07226,342.30666 C 241.55593,340.97503 268.1758,346.10758 292.49248,336.73629 C 320.00213,322.31841 316.52227,270.60184 284.42939,263.93325 C 262.03667,259.50811 238.79902,262.19903 216.07226,261.45743 z M 216.07226,170.70529 C 216.07226,192.876 216.07226,215.04673 216.07226,237.21745 C 240.51996,235.7907 266.45994,241.34543 289.42872,230.99353 C 311.75867,217.69824 305.81714,176.5059 278.88891,172.7747 C 258.2338,169.10092 236.97904,171.31245 216.07226,170.70529 z M 186.4251,146.1697 C 219.40602,146.93306 252.59786,144.3311 285.39714,148.16521 C 311.20962,151.24474 334.61254,173.40298 332.75287,200.70967 C 334.66315,223.18779 318.97588,246.05017 295.76732,247.8594 C 328.454,252.43756 348.32075,287.83147 340.60486,318.99972 C 335.50872,352.13417 299.67051,367.53675 269.63168,366.77933 C 241.897,366.92226 214.16051,366.80319 186.4251,366.84227 C 186.4251,293.28474 186.4251,219.72722 186.4251,146.1697 z"
+ id="text3366" />
+ </g>
+</svg>
View
125 geocamLens/media_src/icons/commandpost.svg
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="commandpost.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ style="display:inline">
+ <defs
+ id="defs9131">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 256 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="512 : 256 : 1"
+ inkscape:persp3d-origin="256 : 170.66667 : 1"
+ id="perspective15" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3272">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3274" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3276" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3272"
+ id="linearGradient3291"
+ gradientUnits="userSpaceOnUse"
+ x1="91.338028"
+ y1="407.77298"
+ x2="254.71867"
+ y2="474.17188"
+ gradientTransform="matrix(0.6361607,1.4300806,1.4300806,-0.6361607,-612.91508,155.69626)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3272"
+ id="linearGradient2390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.6361607,1.4300806,1.4300806,-0.6361607,-612.91508,155.69626)"
+ x1="91.338028"
+ y1="407.77298"
+ x2="254.71867"
+ y2="474.17188" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="591.16955"
+ inkscape:cy="99.016739"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="1280"
+ inkscape:window-height="725"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:snap-global="true"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2480"
+ visible="true"
+ enabled="true"
+ dotted="false"
+ spacingx="2px"
+ spacingy="2px"
+ empspacing="8" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <path
+ style="fill:#4473dc;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 16.593842,16.593842 C 176.19795,16.593842 335.80205,16.593842 495.40616,16.593842 C 495.40616,176.19795 495.40616,335.80205 495.40616,495.40616 C 335.80205,495.40616 176.19795,495.40616 16.593842,495.40616 C 16.593842,335.80205 16.593842,176.19795 16.593842,16.593842 z"
+ id="rect2391" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+ d="M 50.609379,40.540688 C 190.30062,180.08045 329.89929,319.71282 469.47184,459.37129 C 469.47185,319.75047 469.47184,180.12965 469.47184,40.508823 C 329.85102,40.518169 190.23019,40.483699 50.609379,40.540688 z"
+ id="rect3215" />
+ <path
+ style="opacity:0.52558139;fill:url(#linearGradient2390);fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 27.405304,410.68256 C 118.83405,367.45187 197.56728,302.24036 272.39586,235.15161 C 329.62916,181.65821 386.82693,126.70881 430.99594,61.623568 C 444.58209,47.617187 449.21643,17.587172 420.53014,25.935995 C 289.37878,25.767272 158.22747,26.039827 27.076104,26.023989 C 26.565139,108.55794 27.053015,191.09628 27.212701,273.63087 C 27.269647,263.7373 27.270754,227.79538 27.318967,255.1051 C 27.353365,306.96425 27.377887,358.82342 27.405304,410.68256 z"
+ id="path3270" />
+ </g>
+</svg>
View
413 geocamLens/media_src/icons/damaged.svg
@@ -0,0 +1,413 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="damaged.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ style="display:inline">
+ <defs
+ id="defs9131">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3272">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3274" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3276" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient9800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.8360895,-1.0826748,1.4792762,0.8875321,-546.20509,782.54397)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient9798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.403551,1.3080469,-1.7110564,-0.3085016,1140.8394,-251.31795)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4926">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop4928" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop4930" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective9137" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient9809"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1861.2161,610.14475)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient9812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.5010298,2.3886089,-3.2415573,-1.5267822,2048.5483,-548.05304)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient9822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.5010298,2.3886089,-3.2415573,-1.5267822,2048.5483,-548.05304)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient9824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1861.2161,610.14475)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective2466" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(-1.4581983,2.3534856,-3.1490603,-1.5043316,2001.5625,-533.99456)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5321"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1856.5324,610.14475)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5318"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(-1.5010298,2.3886089,-3.2415573,-1.5267822,2048.5483,-548.05304)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3199"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1861.2161,610.14475)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3197"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <inkscape:perspective
+ id="perspective3195"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(-0.403551,1.3080469,-1.7110564,-0.3085016,1140.8394,-251.31795)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3187"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(0.8360895,-1.0826748,1.4792762,0.8875321,-546.20509,782.54397)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3185"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3272"
+ id="linearGradient3278"
+ x1="-165.66502"
+ y1="411.9949"
+ x2="178.48103"
+ y2="588.61072"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3272"
+ id="linearGradient3291"
+ gradientUnits="userSpaceOnUse"
+ x1="91.338028"
+ y1="407.77298"
+ x2="254.71867"
+ y2="474.17188" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="588.61072"
+ x2="178.48103"
+ y1="411.9949"
+ x1="-165.66502"
+ id="linearGradient2569"
+ xlink:href="#linearGradient3272"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2567"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.8360895,-1.0826748,1.4792762,0.8875321,-546.20509,782.54397)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2565"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-0.403551,1.3080469,-1.7110564,-0.3085016,1140.8394,-251.31795)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective2563" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2561"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1861.2161,610.14475)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2559"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.5010298,2.3886089,-3.2415573,-1.5267822,2048.5483,-548.05304)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2557"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1856.5324,610.14475)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2555"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(-1.4581983,2.3534856,-3.1490603,-1.5043316,2001.5625,-533.99456)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ <inkscape:perspective
+ id="perspective2553"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1861.2161,610.14475)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2551"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(-1.5010298,2.3886089,-3.2415573,-1.5267822,2048.5483,-548.05304)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2549"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(-1.5010298,2.3886089,-3.2415573,-1.5267822,2048.5483,-548.05304)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2547"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(2.2418496,-1.7092599,2.4713142,2.5557978,-1861.2161,610.14475)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2545"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <inkscape:perspective
+ id="perspective2543"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(-0.403551,1.3080469,-1.7110564,-0.3085016,1140.8394,-251.31795)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2535"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="291.64789"
+ x2="570"
+ y1="291.64789"
+ x1="391.42859"
+ gradientTransform="matrix(0.8360895,-1.0826748,1.4792762,0.8875321,-546.20509,782.54397)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2533"
+ xlink:href="#linearGradient4926"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="-286.4248"
+ inkscape:cy="142.19157"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="1280"
+ inkscape:window-height="725"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:snap-global="true"
+ showborder="false"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2480"
+ visible="true"
+ enabled="true"
+ dotted="false"
+ spacingx="2px"
+ spacingy="2px"
+ empspacing="8" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ style="display:inline">
+ <path
+ style="fill:#ff8700;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+ d="M 11.21252,11.212518 C 11.21252,174.40417 11.21252,337.59582 11.21252,500.78748 C 174.40417,500.78748 337.59582,500.78748 500.78748,500.78748 C 500.78748,337.59582 500.78748,174.40417 500.78748,11.212518 C 337.59582,11.212518 174.40417,11.212518 11.21252,11.212518 z M 91.693291,93.865509 C 182.14397,93.865509 272.59464,93.865509 363.04532,93.865509 C 272.59464,183.12038 182.14397,272.37525 91.693291,361.63013 C 91.693291,272.37525 91.693291,183.12038 91.693291,93.865509 z M 420.3067,150.36987 C 420.3067,239.62475 420.3067,328.87962 420.3067,418.13449 C 329.85602,418.13449 239.40534,418.13449 148.95467,418.13449 C 239.40534,328.87962 329.85602,239.62475 420.3067,150.36987 z"
+ id="rect2405" />
+ </g>
+</svg>
View
90 geocamLens/media_src/icons/downlink.svg
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="downlink.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs9131">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 256 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="512 : 256 : 1"
+ inkscape:persp3d-origin="256 : 170.66667 : 1"
+ id="perspective12" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.49497476"
+ inkscape:cx="-24.465667"
+ inkscape:cy="318.84799"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="964"
+ inkscape:window-height="721"
+ inkscape:window-x="304"
+ inkscape:window-y="25"
+ inkscape:snap-global="false"
+ showborder="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid9160"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#4473dc;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 16.593842,16.593842 C 176.19795,16.593842 335.80205,16.593842 495.40616,16.593842 C 495.40616,176.19795 495.40616,335.80205 495.40616,495.40616 C 335.80205,495.40616 176.19795,495.40616 16.593842,495.40616 C 16.593842,335.80205 16.593842,176.19795 16.593842,16.593842 z"
+ id="rect2391" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:19.99999809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 70.622147,70.622147 C 194.20738,70.622147 317.79261,70.622147 441.37784,70.622147 C 441.37784,194.20738 441.37784,317.79261 441.37784,441.37784 C 317.79261,441.37784 194.20738,441.37784 70.622147,441.37784 C 70.622147,317.79261 70.622147,194.20738 70.622147,70.622147 z"
+ id="rect8318" />
+ <path
+ style="fill:#4473dc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:30;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 58.375,100.15625 L 33.0625,116.25 C 63.642163,164.40769 94.201585,212.56107 124.78125,260.71875 L 137.28125,280.375 L 150,260.875 C 170.6515,229.23128 191.31724,197.58123 211.96875,165.9375 C 285.71395,270.23342 359.47354,374.54784 433.21875,478.84375 L 457.71875,461.5 C 379.72392,351.19395 301.71357,240.8998 223.71875,130.59375 L 210.96875,112.5625 L 198.90625,131.0625 C 178.47397,162.37031 158.05727,193.66095 137.625,224.96875 C 111.20756,183.36585 84.792438,141.75916 58.375,100.15625 z"
+ id="path9162" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:35;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 37.09375,20.9375 L 7.34375,39.375 C 45.667914,101.3381 83.988338,163.2869 122.3125,225.25 L 136.8125,248.6875 L 151.84375,225.625 C 171.83375,194.99489 191.8225,164.34886 211.8125,133.71875 C 298.19936,255.38381 384.61315,377.05368 471,498.71875 L 499.53125,478.4375 C 408.18327,349.7853 316.84799,221.1522 225.5,92.5 L 210.625,71.53125 L 196.5625,93.0625 C 176.9066,123.18067 157.24964,153.28809 137.59375,183.40625 C 104.10134,129.2552 70.586161,75.08855 37.09375,20.9375 z"
+ id="path9160" />
+ </g>
+</svg>
View
113 geocamLens/media_src/icons/droppoint.svg
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="512"
+ height="512"
+ id="svg9129"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docname="droppoint.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs9131">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 256 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="512 : 256 : 1"
+ inkscape:persp3d-origin="256 : 170.66667 : 1"
+ id="perspective17" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient4926">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop4928" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop4930" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4926"
+ id="linearGradient2400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.4581983,2.3534856,3.1490603,-1.5043316,-1489.5625,-533.99456)"
+ x1="391.42859"
+ y1="291.64789"
+ x2="570"
+ y2="291.64789" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.25"
+ inkscape:cx="607.58942"
+ inkscape:cy="524.51442"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ width="512px"
+ inkscape:window-width="638"
+ inkscape:window-height="721"
+ inkscape:window-x="420"
+ inkscape:window-y="25"
+ inkscape:snap-global="false"
+ showborder="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid9160"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata9134">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 27.075566,317.3401 C 60.935309,443.70639 190.97382,518.78416 317.34011,484.92442 C 443.70639,451.06467 518.78417,321.02616 484.92442,194.65988 C 451.06468,68.293591 321.02616,-6.7841814 194.65988,27.075562 C 68.293596,60.935305 -6.7841769,190.97382 27.075566,317.3401 z"
+ id="path3253" />
+ <path
+ style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:20;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 34.930364,256 C 34.930364,378.03043 133.96956,477.06962 255.99999,477.06962 C 378.03042,477.06962 477.06961,378.03043 477.06961,256 C 477.06961,133.96957 378.03042,34.930379 255.99999,34.930379 C 133.96956,34.930379 34.930364,133.96957 34.930364,256 z"
+ id="path9258" />
+ <g
+ id="g10974"
+ transform="translate(20,-12.550377)">
+ <path
+ style="font-size:45.16031647px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Sans;-inkscape-font-specification:Sans"
+ d="M 614.51959,194.39131 C 614.51959,241.97167 614.51959,289.55203 614.51959,337.13239 C 642.40874,337.45671 674.2639,338.63746 694.92099,316.4437 C 715.24168,293.86108 716.02859,258.96791 705.98746,231.64966 C 694.41654,202.59216 660.71289,192.93503 632.02136,194.39131 C 626.18744,194.39131 620.35352,194.39131 614.51959,194.39131 z M 554.19543,148.79564 C 599.528,149.6617 645.40518,145.79446 690.16469,154.67389 C 733.96413,163.2895 769.33608,202.10887 773.21557,246.67316 C 778.58749,286.29106 767.67679,330.71902 734.59006,355.80068 C 700.19545,383.72423 653.45526,382.13094 611.62862,382.72804 C 592.48422,382.72803 573.33983,382.72803 554.19543,382.72803 C 554.19543,304.75056 554.19543,226.7731 554.19543,148.79564 z M 820.56189,148.79564 C 859.26977,149.15443 898.02287,148.01345 936.69745,149.49532 C 967.6533,151.30382 1001.2039,167.97592 1009.7578,199.98317 C 1018.8688,231.74616 1010.6151,271.90133 979.49233,288.53156 C 949.46091,305.70504 913.99505,300.77739 880.88605,301.56461 C 880.88605,328.61908 880.88605,355.67355 880.88605,382.72803 C 860.77799,382.72803 840.66994,382.72803 820.56189,382.72803 C 820.56189,304.75056 820.56189,226.7731 820.56189,148.79564 z M 880.88605,192.51106 C 880.88605,214.29044 880.88605,236.06981 880.88605,257.84918 C 899.31219,256.83168 918.76265,260.88662 936.08844,253.08992 C 959.90067,240.36443 954.36392,197.19639 926.29799,193.70104 C 911.30854,191.49728 896.00444,192.91387 880.88605,192.51106 z"
+ id="text9769" />
+ </g>
+ </g>