diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f836aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +*.pyc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e8d9f8f --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +##MapFart -- Copyright 2013 Aaron Racicot + +http://opensource.org/licenses/BSD-2-Clause + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/default.cfg b/default.cfg new file mode 100644 index 0000000..486d67e --- /dev/null +++ b/default.cfg @@ -0,0 +1,13 @@ +####################################### +########APP Settings ########## +####################################### + +# enable debug mode. Disable this in production! +DEBUG = False + +TITLE = 'App Title' +SUBTITLE = 'Website' +AUTHOR = 'Aaron Racicot' +AUTHOR_EMAIL = 'aaronr@z-pulley.com' +KEYWORDS = 'z-pulley, gis, python' +DESCRIPTION = 'A simple site' diff --git a/examples/geojson.json b/examples/geojson.json new file mode 100644 index 0000000..4c87b59 --- /dev/null +++ b/examples/geojson.json @@ -0,0 +1,33 @@ +{ "type": "FeatureCollection", + "features": [ + { "type": "Feature", + "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, + "properties": {"prop0": "value0"} + }, + { "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] + ] + }, + "properties": { + "prop0": "value0", + "prop1": 0.0 + } + }, + { "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], + [100.0, 1.0], [100.0, 0.0] ] + ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + } + ] +} diff --git a/examples/leaflet.json b/examples/leaflet.json new file mode 100644 index 0000000..5cefe9e --- /dev/null +++ b/examples/leaflet.json @@ -0,0 +1,29 @@ +{ "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "name": "Coors Field", + "amenity": "Baseball Stadium", + "popupContent": "This is where the Rockies play!" + }, + "geometry": { + "type": "Point", + "coordinates": [-104.99404, 39.75621] + } + }, + { + "type": "Feature", + "properties": {"party": "Republican"}, + "geometry": { + "type": "Polygon", + "coordinates": [[ + [-104.05, 48.99], + [-97.22, 48.98], + [-96.58, 45.94], + [-104.03, 45.94], + [-104.05, 48.99] + ]] + } + } +]} \ No newline at end of file diff --git a/local.cfg b/local.cfg new file mode 100644 index 0000000..8ca769c --- /dev/null +++ b/local.cfg @@ -0,0 +1,13 @@ +####################################### +########APP Settings ########## +####################################### + +# enable debug mode. Disable this in production! +DEBUG = True + +TITLE = 'MapFart' +SUBTITLE = 'Quick map of your data...' +AUTHOR = 'Aaron Racicot' +AUTHOR_EMAIL = 'aaronr@z-pulley.com' +KEYWORDS = 'api, z-pulley, gis, python, aaron, racicot' +DESCRIPTION = 'Quick Mapping API' diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/mapfart.conf b/mapfart.conf new file mode 100644 index 0000000..441f49f --- /dev/null +++ b/mapfart.conf @@ -0,0 +1,34 @@ + + ServerName www.mapfart.com + Redirect permanent / http://mapfart.com/ + + + + ServerName mapfart.com + ServerAdmin aaronr@z-pulley.com + + LogLevel info + ErrorLog /home/projects/mapfart/logs/error.log + CustomLog /home/projects/mapfart/logs/access.log combined + + #ErrorDocument 401 /Forbidden.html + #ErrorDocument 403 /Forbidden.html + #ErrorDocument 404 /FileNotFound.html + #ErrorDocument 500 /cgi-bin/ServerError.pl + + DocumentRoot /home/projects/mapfart/www + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Order allow,deny + allow from all + + + WSGIDaemonProcess wsgi_mapfart threads=15 processes=15 \ + display-name=wsgi_mapfart \ + python-path=/home/projects/mapfart/venv/lib/python2.7/site-packages + WSGIProcessGroup wsgi_mapfart + + WSGIScriptAlias / /home/projects/mapfart/mapfart.wsgi + + \ No newline at end of file diff --git a/mapfart.py b/mapfart.py new file mode 100644 index 0000000..f6d0230 --- /dev/null +++ b/mapfart.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +from flask import Flask, request, jsonify +from flask import make_response, render_template +from flask import flash, redirect, url_for, session, escape, g + +# General web serving +from mapfartapi.web import index, documentation, api_landing +# The good old fart generate code +from mapfartapi.api import fart, fart_srid, fart_srid_xy, fart_default +# The fart serve code +from mapfartapi.api import fart_serve +# Testing... +from mapfartapi.api import testcurl + +app = Flask(__name__) + +app.config.from_pyfile('default.cfg') +app.config.from_pyfile('local.cfg') + +# URLs +# Landing Page +app.add_url_rule('/', 'index', index) +# Fart Serve +app.add_url_rule('/fart_', 'fart_serve', fart_serve, methods=['GET']) +# Building farts +# Default farts +app.add_url_rule('/api/fart', 'fart_default', fart_default, methods=['POST']) +# Reprojected farts +app.add_url_rule('/api//fart', 'fart_srid', fart_srid, methods=['POST']) +# Reprojected farts of any size +app.add_url_rule('/api////fart', 'fart_srid_xy', fart_srid_xy, methods=['POST']) + +# Docs (in the future) +app.add_url_rule('/api', 'api', api_landing) +app.add_url_rule('/documentation', 'documentation', documentation) + +# Testing +app.add_url_rule('/api/testcurl', 'testcurl', testcurl, methods=['POST']) + +@app.errorhandler(500) +def page_not_found(e): + return "your farts stink... try again!\n", 500 + +@app.errorhandler(404) +def page_not_found(e): + return "Your fart request could not be served...\n", 404 + +if __name__ == "__main__": + app.run(debug=True,host='0.0.0.0') diff --git a/mapfart.wsgi b/mapfart.wsgi new file mode 100644 index 0000000..ec8ca0a --- /dev/null +++ b/mapfart.wsgi @@ -0,0 +1,6 @@ +import sys +sys.path.insert(0, "/home/projects/mapfart") +sys.path.insert(0, "/usr/local/lib/geom-0.2/bin") +sys.path.insert(0, "/usr/local/bin") + +from mapfart import app as application diff --git a/mapfartapi/__init__.py b/mapfartapi/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mapfartapi/api.py b/mapfartapi/api.py new file mode 100644 index 0000000..efe404f --- /dev/null +++ b/mapfartapi/api.py @@ -0,0 +1,247 @@ +from flask import abort, request, render_template, jsonify, send_file +import psycopg2 +import sys +import os +import json +import re +import subprocess +import tempfile + +from utils.jsonp import jsonp +from shapely.geometry import shape +from shapely.ops import cascaded_union +from geojson import Feature, FeatureCollection, dumps + +from shapely.wkb import loads + +def fart_serve(fart_id): + if request.method == 'GET': + try: + tf = open("/tmp/fart_"+fart_id+".png", "r") + return send_file(tf, mimetype='image/png') + except Exception, e: + abort(404) + abort(404) + +# Yummy default farts +def fart_default(): + if request.method == 'POST': + return fart() + +# Override the srid +def fart_srid(srid): + if request.method == 'POST': + return fart(srid=srid) + +# Override the srid and size +def fart_srid_xy(srid,xsize,ysize): + if request.method == 'POST': + return fart(srid=srid, xsize=xsize, ysize=ysize) + +@jsonp +def fart(srid='4326', xsize='800', ysize='600'): + # Only POST is accepted + if request.method == 'POST': + data = request.data + # Test code + # return jsonify({'data': data, 'ct': request.environ['HTTP_CONTENT_TYPE']}) + + # We recieved a GeoJSON payload + if data.startswith('{'): + try: + js = json.loads(data) + return process_geojson(js, srid, xsize, ysize) + except Exception, e: + abort(404) + # WKT Payload + elif re.match(r'^[PLM\"]',data): + # Strip off "'s if they are there + if data.endswith("\""): + data = data[:-1] + if data.startswith("\""): + data = data[1:] + return process_wkt(data,xsize,ysize) + # WKB Payload + elif re.match(r'^[01]*$',data.strip()): + return process_wkb(data,xsize,ysize) + + else: + # We dont know what this data is.... + # return jsonify({'unknown data': data}) + abort(500) + +def process_wkb(data,xsize,ysize): + try: + wkbdata = loads(data.strip().decode("hex")) + tf = tempfile.NamedTemporaryFile(prefix='fart_', suffix='.png',delete=False) + cmd = "/usr/local/lib/geom-0.2/bin/geom draw -w %s -h %s -f %s -g '%s'" % (xsize, ysize, tf.name, wkbdata.wkt) + # return jsonify({'cmd':cmd}) + + proc = subprocess.Popen( + cmd, + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + # wait for processing to finish + stdout_value, stderr_value = proc.communicate() + if stderr_value: + # return jsonify({'stdout':stdout_value, 'stderr':sderr_value}) + # log.error('stderr_value: %s' % stderr_value) + abort(500) + except Exception, e: + # return jsonify({'stdout':stdout_value, 'stderr':sderr_value}) + abort(500) + return "http://mapfart.com/" + os.path.splitext(os.path.basename(tf.name))[0] + "\n" + + + +def process_wkt(data,xsize,ysize): + try: + tf = tempfile.NamedTemporaryFile(prefix='fart_', suffix='.png',delete=False) + cmd = "/usr/local/lib/geom-0.2/bin/geom draw -w %s -h %s -f %s -g '%s'" % (xsize, ysize, tf.name, data) + proc = subprocess.Popen( + cmd, + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + # wait for processing to finish + stdout_value, stderr_value = proc.communicate() + if stderr_value: + # return jsonify({'stdout':stdout_value, 'stderr':sderr_value}) + # log.error('stderr_value: %s' % stderr_value) + abort(500) + except Exception, e: + # return jsonify({'stdout':stdout_value, 'stderr':sderr_value}) + abort(500) + return "http://mapfart.com/" + os.path.splitext(os.path.basename(tf.name))[0] + "\n" + + +def process_geojson(js, srid, xsize, ysize): + shapes = [] + points = [] + lines = [] + polygons = [] + count = 0 + for f in js['features']: + count = count + 1 + myShape = shape(f['geometry']) + shapes.append(myShape) + if re.match(r'Point', myShape.geom_type) or re.match(r'MultiPoint', myShape.geom_type): + myFeature = Feature(id=count, + geometry=myShape, + properties = {"name": "foo1"}) + points.append(myFeature) + elif re.match(r'Line', myShape.geom_type) or re.match(r'MultiLine', myShape.geom_type): + myFeature = Feature(id=count, + geometry=myShape, + properties = {"name": "foo2"}) + lines.append(myFeature) + elif re.match(r'Polygon', myShape.geom_type) or re.match(r'MultiPolygon', myShape.geom_type): + myFeature = Feature(id=count, + geometry=myShape, + properties = {"name": "foo3"}) + polygons.append(myFeature) + bbox = cascaded_union(shapes).bounds + + if srid != 4326: + if srid==3857 or srid==900913: + ymin = bbox[1] + ymax = bbox[3] + # for spherical mercator clamp to 85 deg north and south + if ymin < -85.0: + ymin = -85.0 + if ymax > 85.0: + ymax = 85.0 + + bbox = (bbox[0],ymin,bbox[2],ymax) + + # we must translate the bbox to the output projection + connstring="dbname='projfinder' port=5432 user='mapfart' host='localhost' password='mapfart'" + try: + conn=psycopg2.connect(connstring) + cursor=conn.cursor() + sql = "select st_x(st_transform(st_geometryfromtext('POINT(%s %s)',4326),%s)) as xmin, st_y(st_transform(st_geometryfromtext('POINT(%s %s)',4326),%s)) as ymin, st_x(st_transform(st_geometryfromtext('POINT(%s %s)',4326),%s)) as xmax, st_y(st_transform(st_geometryfromtext('POINT(%s %s)',4326),%s)) as ymax" % (bbox[0],bbox[1],srid,bbox[0],bbox[1],srid,bbox[2],bbox[3],srid,bbox[2],bbox[3],srid) + cursor.execute(sql) + results = cursor.fetchone() + bbox_string = " ".join(str(b) for b in results) + except Exception, e: + abort(500) + else: + bbox_string = " ".join(str(b) for b in bbox) + + # Monkey with the image size to get the aspect ratio about the same as the extent of the data + data_aspect = ((bbox[2] - bbox[0])/2) / (bbox[3] - bbox[1]) + if data_aspect>=1: + ysize = float(xsize) / data_aspect + else: + xsize = float(ysize) * data_aspect + + tf_points = tempfile.NamedTemporaryFile(prefix='fart_pt_', suffix='.json', delete=False) + tf_lines = tempfile.NamedTemporaryFile(prefix='fart_ln_', suffix='.json', delete=False) + tf_polygons = tempfile.NamedTemporaryFile(prefix='fart_poly_', suffix='.json', delete=False) + layers_to_draw = "" + if len(points) > 0: + tf_points.write(dumps(FeatureCollection(points))) + layers_to_draw = layers_to_draw + "points " + if len(lines) > 0: + tf_lines.write(dumps(FeatureCollection(lines))) + layers_to_draw = layers_to_draw + "lines " + if len(polygons) > 0: + tf_polygons.write(dumps(FeatureCollection(polygons))) + layers_to_draw = layers_to_draw + "polygons " + tf_points.flush() + tf_points.close() + tf_lines.flush() + tf_lines.close() + tf_polygons.flush() + tf_polygons.close() + + # Now that we have our bounds and up to 3 files (points, lines, polygons) we + # can have mapserver kick out an image. + mapfile = render_template("mapfart.map", point_name = tf_points.name, + line_name = tf_lines.name, + polygon_name = tf_polygons.name, + srid = srid) + tf_mapfile = tempfile.NamedTemporaryFile(prefix='fart_', suffix='.map', delete=False) + tf_mapfile.write(mapfile) + tf_mapfile.flush() + tf_mapfile.close() + + tf_png = tempfile.NamedTemporaryFile(prefix='fart_', suffix='.png',delete=False) + + try: + cmd = "/usr/local/bin/shp2img -m %s -o %s -l '%s' -s %s %s -e %s" % (tf_mapfile.name, tf_png.name, layers_to_draw.strip(), str(xsize), str(ysize), bbox_string) + proc = subprocess.Popen( + cmd, + shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + # wait for processing to finish + stdout_value, stderr_value = proc.communicate() + if stderr_value: + pass + # return jsonify({'stdout':stdout_value, 'stderr':stderr_value}) + # log.error('stderr_value: %s' % stderr_value) + except Exception, e: + abort(500) + return "http://mapfart.com/" + os.path.splitext(os.path.basename(tf_png.name))[0] + "\n" + + + +def testcurl(): + if request.method == 'POST': + # data = request.form.keys()[0] + data = request.data + foo = '' + if len(request.args) > 0 and request.args['foo']: + foo = request.args['foo'] + return jsonify({'data':data, 'foo':foo}) + else: + abort(404) + diff --git a/mapfartapi/web.py b/mapfartapi/web.py new file mode 100644 index 0000000..939da76 --- /dev/null +++ b/mapfartapi/web.py @@ -0,0 +1,11 @@ +from flask import render_template + +def index(): + return render_template('index.html') + +def documentation(): + return render_template('documentation.html') + +def api_landing(): + return render_template('api_landing.html') + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..17671d9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +Flask +Flask-Script +WTForms +Flask-WTF + +psycopg2 + +Fiona +Shapely +geojson diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..c248904 --- /dev/null +++ b/setup.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# This script should be run whenever new packages are installed to ensure +# things are set for future runs, and of course to setup a new virtualenv +VENV_BASE=$(pwd) +VENV_SUBDIR='venv' +CMD=pip +REQ="requirements.txt" +EGG_CACHE="egg-cache" +APACHE_USER="www-data" +which virtualenv &>/dev/null +if [ $? -ne 0 ]; then + echo "virtualenv command not found" + sudo easy_install virtualenv + exit "Install virtualenv..." +fi + +if [ -d "${VENV_SUBDIR}" ]; then + echo "virtualenv has already been created" +else + virtualenv --no-site-packages "${VENV_SUBDIR}" +fi +source "${VENV_SUBDIR}/bin/activate" +easy_install pip + +if [ -f "$REQ" ]; then + $CMD_PREFIX $CMD install $EXTRA_ARGS -r $REQ +fi + +if [ ! -d "$EGG_CACHE" ]; then + echo "Creating the egg cache" + mkdir -p "$EGG_CACHE" +fi +sudo chown "$APACHE_USER" "$EGG_CACHE" +echo "To switch to this venv run the command 'source $VENV_SUBDIR/bin/activate'" diff --git a/static/.gitignore b/static/.gitignore new file mode 100644 index 0000000..f058ff0 --- /dev/null +++ b/static/.gitignore @@ -0,0 +1,40 @@ +# Numerous always-ignore extensions +*.diff +*.err +*.orig +*.log +*.rej +*.swo +*.swp +*.vi +*~ +*.sass-cache + +# OS or Editor folders +.DS_Store +.cache +.project +.settings +.tmproj +nbproject +Thumbs.db + +# Dreamweaver added files +_notes +dwsync.xml + +# Komodo +*.komodoproject +.komodotools + +# Folders to ignore +.hg +.svn +.CVS +intermediate +publish +.idea + +# build script local files +build/buildinfo.properties +build/config/buildinfo.properties diff --git a/static/.htaccess b/static/.htaccess new file mode 100644 index 0000000..232c96f --- /dev/null +++ b/static/.htaccess @@ -0,0 +1,503 @@ +# Apache configuration file +# httpd.apache.org/docs/2.2/mod/quickreference.html + +# Note .htaccess files are an overhead, this logic should be in your Apache config if possible +# httpd.apache.org/docs/2.2/howto/htaccess.html + +# Techniques in here adapted from all over, including: +# Kroc Camen: camendesign.com/.htaccess +# perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/ +# Sample .htaccess file of CMS MODx: modxcms.com + + +### +### If you run a webserver other than apache, consider: +### github.com/paulirish/html5-boilerplate-server-configs +### + + + +# ---------------------------------------------------------------------- +# Better website experience for IE users +# ---------------------------------------------------------------------- + +# Force the latest IE version, in various cases when it may fall back to IE7 mode +# github.com/rails/rails/commit/123eb25#commitcomment-118920 +# Use ChromeFrame if it's installed for a better experience for the poor IE folk + + + Header set X-UA-Compatible "IE=Edge,chrome=1" + # mod_headers can't match by content-type, but we don't want to send this header on *everything*... + + Header unset X-UA-Compatible + + + + +# ---------------------------------------------------------------------- +# Cross-domain AJAX requests +# ---------------------------------------------------------------------- + +# Serve cross-domain ajax requests, disabled. +# enable-cors.org +# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity + +# +# Header set Access-Control-Allow-Origin "*" +# + + + +# ---------------------------------------------------------------------- +# Webfont access +# ---------------------------------------------------------------------- + +# Allow access from all domains for webfonts. +# Alternatively you could only whitelist your +# subdomains like "subdomain.example.com". + + + + Header set Access-Control-Allow-Origin "*" + + + + + +# ---------------------------------------------------------------------- +# Proper MIME type for all files +# ---------------------------------------------------------------------- + + +# JavaScript +# Normalize to standard type (it's sniffed in IE anyways) +# tools.ietf.org/html/rfc4329#section-7.2 +AddType application/javascript js + +# Audio +AddType audio/ogg oga ogg +AddType audio/mp4 m4a + +# Video +AddType video/ogg ogv +AddType video/mp4 mp4 m4v +AddType video/webm webm + +# SVG. +# Required for svg webfonts on iPad +# twitter.com/FontSquirrel/status/14855840545 +AddType image/svg+xml svg svgz +AddEncoding gzip svgz + +# Webfonts +AddType application/vnd.ms-fontobject eot +AddType application/x-font-ttf ttf ttc +AddType font/opentype otf +AddType application/x-font-woff woff + +# Assorted types +AddType image/x-icon ico +AddType image/webp webp +AddType text/cache-manifest appcache manifest +AddType text/x-component htc +AddType application/x-chrome-extension crx +AddType application/x-xpinstall xpi +AddType application/octet-stream safariextz +AddType text/x-vcard vcf + + + +# ---------------------------------------------------------------------- +# Allow concatenation from within specific js and css files +# ---------------------------------------------------------------------- + +# e.g. Inside of script.combined.js you could have +# +# +# and they would be included into this single file. + +# This is not in use in the boilerplate as it stands. You may +# choose to name your files in this way for this advantage or +# concatenate and minify them manually. +# Disabled by default. + +# +# Options +Includes +# AddOutputFilterByType INCLUDES application/javascript application/json +# SetOutputFilter INCLUDES +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES text/css +# SetOutputFilter INCLUDES +# + + +# ---------------------------------------------------------------------- +# Gzip compression +# ---------------------------------------------------------------------- + + + +# Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/ + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + +# HTML, TXT, CSS, JavaScript, JSON, XML, HTC: + + FilterDeclare COMPRESS + FilterProvider COMPRESS DEFLATE resp=Content-Type $text/html + FilterProvider COMPRESS DEFLATE resp=Content-Type $text/css + FilterProvider COMPRESS DEFLATE resp=Content-Type $text/plain + FilterProvider COMPRESS DEFLATE resp=Content-Type $text/xml + FilterProvider COMPRESS DEFLATE resp=Content-Type $text/x-component + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/javascript + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/json + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/xml + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/xhtml+xml + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/rss+xml + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/atom+xml + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/vnd.ms-fontobject + FilterProvider COMPRESS DEFLATE resp=Content-Type $image/svg+xml + FilterProvider COMPRESS DEFLATE resp=Content-Type $application/x-font-ttf + FilterProvider COMPRESS DEFLATE resp=Content-Type $font/opentype + FilterChain COMPRESS + FilterProtocol COMPRESS DEFLATE change=yes;byteranges=no + + + + # Legacy versions of Apache + AddOutputFilterByType DEFLATE text/html text/plain text/css application/json + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE text/xml application/xml text/x-component + AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml + AddOutputFilterByType DEFLATE image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype + + + + + +# ---------------------------------------------------------------------- +# Expires headers (for better cache control) +# ---------------------------------------------------------------------- + +# These are pretty far-future expires headers. +# They assume you control versioning with cachebusting query params like +# + + + + + + + + + + + + + + + diff --git a/templates/documentation.html b/templates/documentation.html new file mode 100644 index 0000000..8212b58 --- /dev/null +++ b/templates/documentation.html @@ -0,0 +1,167 @@ + + + + + api.projfinder.com · documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +

About
ProjFinder is brought to you by CUGOS

+

Used as a core learning tool and hack project ProjFinder has helped many CUGOS members learn all aspects of the Web Stack and GeoSpatial technologies.

+
+
+
+ +

Contact
Get in touch... we would love to talk to you

+

+

+ CUGOS (Aaron Racicot)
+ POBox 1614
+ Langley, WA 98260
+ P: (360) 221-2441 +
+ +
+ Information
+ projfinder@cugos.org +
+

+
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3713a58 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,83 @@ + + + + + mapfart.com · Mapfart API + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/mapfart.map b/templates/mapfart.map new file mode 100644 index 0000000..eb119ef --- /dev/null +++ b/templates/mapfart.map @@ -0,0 +1,87 @@ +# Mapfile for MapFart +MAP + NAME "MapFart" + UNITS DD + IMAGETYPE PNG + PROJECTION + "init=epsg:{{srid}}" + END + WEB + IMAGEPATH "/home/projects/mapfart/tmp/" + IMAGEURL "/tmp/" + END + SHAPEPATH "/tmp" + CONFIG "MS_ERRORFILE" "/home/projects/mapfart.com/ms_error.txt" + SYMBOL + NAME "circle" + TYPE ellipse + FILLED true + POINTS + 1 1 + END + END + + LAYER + NAME "polygons" + CONNECTIONTYPE OGR + CONNECTION "{{polygon_name}}" + PROJECTION + "init=epsg:4326" + END + TYPE polygon + STATUS ON + OPACITY 100 + CLASS + NAME "default" + STYLE + OPACITY 100 + OUTLINECOLOR 0 0 0 + WIDTH 2 + END # END STYLE + STYLE + OPACITY 20 + COLOR 201 0 0 + END # END STYLE + END # END CLASS + END # END LAYER + + LAYER + NAME "lines" + CONNECTIONTYPE OGR + CONNECTION "{{line_name}}" + PROJECTION + "init=epsg:4326" + END + TYPE line + STATUS ON + OPACITY 100 + CLASS + NAME "default" + STYLE + WIDTH 2 + COLOR 0 0 100 + END # END STYLE + END # END CLASS + END # END LAYER + + LAYER + NAME "points" + CONNECTIONTYPE OGR + CONNECTION "{{point_name}}" + PROJECTION + "init=epsg:4326" + END + TYPE point + STATUS ON + OPACITY 100 + CLASS + NAME "default" + STYLE + SYMBOL "circle" + SIZE 10 + COLOR 101 101 100 + END # END STYLE + END # END CLASS + END # END LAYER + +END # END MAP diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/utils/jsonp.py b/utils/jsonp.py new file mode 100644 index 0000000..f3da62f --- /dev/null +++ b/utils/jsonp.py @@ -0,0 +1,17 @@ +from functools import wraps +from flask import request, current_app + + +def jsonp(func): + """Wraps JSONified output for JSONP requests.""" + @wraps(func) + def decorated_function(*args, **kwargs): + callback = request.args.get('callback', False) + if callback: + data = str(func(*args, **kwargs).data) + content = str(callback) + '(' + data + ')' + mimetype = 'application/javascript' + return current_app.response_class(content, mimetype=mimetype) + else: + return func(*args, **kwargs) + return decorated_function diff --git a/www/.gitignore b/www/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/www/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore