Tag Archives: GIS

Use GIS to Fill a Form

27 Feb

We use maps to answer questions and the information taken from a map may need to find its way somewhere else. In this post, I will show you how to take a map and populate form data with the properties in a GIS feature class just by clicking a location on the map.

The Application

1

The application will consist of a form with a button to launch a map. When the map opens, the user will click on a parcel. The map will make a call to an Albuquerque Open Data GIS service and retrieve the information for the parcel. The map will send the data to the form.

The Code

The form is a simple Bootstrap form.

<form class=”form-horizontal” role=”form”>
<div class=”form-group”>
<label class=”control-label col-sm-2″ for=”email”>Street Number:</label>
<div class=”col-sm-10″>
<input type=”text” class=”form-control” id=”snumber” placeholder=”Enter Street Number”>
</div>
</div>
<div class=”form-group”>
<label class=”control-label col-sm-2″ for=”pwd”>Street Name:</label>
<div class=”col-sm-10″>
<input type=”text” class=”form-control” id=”sname” placeholder=”Enter Street Name”>
</div>
</div>
<div class=”form-group”>
<label class=”control-label col-sm-2″ for=”email”>Street Designation:</label>
<div class=”col-sm-10″>
<input type=”text” class=”form-control” id=”sdes” placeholder=”Enter Street Designation”>
</div>
</div>
<div class=”form-group”>
<label class=”control-label col-sm-2″ for=”pwd”>Street Quadrant:</label>
<div class=”col-sm-10″>
<input type=”text” class=”form-control” id=”squad” placeholder=”Enter Street Quadrant”>
</div>
</div>
<div class=”form-group”>
<div class=”col-sm-offset-2 col-sm-10″>
<button type=”submit” onclick=”openMap()” class=”btn btn-default”>Open Map</button>
</div>
</div>
</form>

The button calls a JavaScript function which opens the map in a new window.

function openMap(){window.open(“fillformmap.html”,”Map”,”width=600,height=600″);}

The goal is to fill the form with data from the map. The map that opens is a standard Leaflet.js map. I use an on click function to create a buffer – just to make sure we hit a parcel – and send the buffer to the service query and ask for the parcel information back (for more information on the buffer with Turf.js see my post on Albuquerque Incidents).

map.on(“click”,function(e){

a=L.marker(e.latlng);
b=a.toGeoJSON();
buffered = turf.buffer(b,.005,”miles”);
L.geoJson(buffered).addTo(map);
var result = turf.featurecollection(buffered.features.concat(b));

g='{“rings”:’+JSON.stringify(buffered.features[0].geometry.coordinates)+’}’;

var params=”f=json&outSR=4326&outFields=*&geometryType=esriGeometryPolygon&spatialRel=esriSpatialRelIntersects&inSR=4326&geometry=”+g;

var url = “http://coagisweb.cabq.gov/arcgis/rest/services/public/fullviewer/MapServer/9/query&#8221;;
console.log(params);
http=new XMLHttpRequest();
http.open(“POST”, url, true);
http.setRequestHeader(“Content-type”, “application/x-www-form-urlencoded”);
http.onreadystatechange = function() {//Call a function when the state changes.
if(http.readyState == 4 && http.status == 200) {
console.log(http.responseText);
var result= JSON.parse(http.responseText);
……..CODE GOES HERE….
}}

http.send(params);
});

Nothing special about the code above, it is been used in several previous posts. To send the data to the form, we need to use the code below.

opener.document.getElementById(“sname”).value=result.features[0].attributes.STREETNAME;
opener.document.getElementById(“snumber”).value=result.features[0].attributes.STREETNUMBER;
opener.document.getElementById(“squad”).value=result.features[0].attributes.STREETQUADRANT;
opener.document.getElementById(“sdes”).value=result.features[0].attributes.STREETDESIGNATION;
window.close();

The above code grabs the text boxes by ID for the document that opened the map. It sets the value of those text boxes to the value returned from the map query. Now we have a completed form.

2 Sometimes clicking a map is better than filling out information by hand. I only queried a single GIS feature, but do not think that is the limit. We could easily query a number of layers to populate a form. Just add another AJAX call and pass the results.

Advertisements

Live Scenario Planning in Leaflet.js with Turf.js

22 Jan

When I started scenario planning, I had to run my data through a model to get some output. To try a new scenario, I had to rerun the model with new data and then compare the outputs. The process was slow. With the tools currently available, we can do better. I wrote a simpl live scenario planning application using Leaflet.js using Turf.js as the “model”. I put model in quotes because for the example we will simply sum a field in each marker.

The application showing the values of two fields in our data.

The application showing the values of two fields in our data.

The above image is my application. It takes points in Albuquerque and sums two different fields and displays the results in a bar chart below. The application starts as follows:

1. Create a Leaflet.js Map.

var map = L.map(‘map’, {
center: [35.10418, -106.62987],
zoom:15
});

L.tileLayer(‘http://{s}.tile.osm.org/{z}/{x}/{y}.png’).addTo(map);

2. Add my base scenario points and a polygon for the area of interest.

var p1 = turf.point(-106,35, {“marker-color”: “#6BC65F”,”title”: 100, “description”: “Not in Polygon”, “someOtherProperty”:3 });
var p2 = turf.point(-106.62987,35.10418, {“marker-color”: “#6BC65F”,”title”: 4, “description”: “In Polygon”, “someOtherProperty”:6 });
var p3 = turf.point(-106.64429,35.14125, {“marker-color”: “#6BC65F”,”title”: 1, “description”: “Also in Polygon”, “someOtherProperty”:5 });

var polygon = turf.polygon([ [
[-106.73355,35.21197],[-106.73355,35.04911],[ -106.51932,35.04911],[-106.49872,35.19177]
]], {
“fill”: “#6BC65F”,
“stroke”: “#6BC65F”,
“stroke-width”: 5,
“title”:”Polygon”,
“description”:”A sample polygon”
});

var p = turf.featurecollection([polygon]);
var t = turf.featurecollection([p1,p2,p3]);

3. Add the Turf.js points to the map as GeoJSON in Leaflet.js

function onEachFeature(feature, layer) {

layer.bindPopup(“<h3>Add this number: “+feature.properties.title+”</h3>”+feature.properties.description);
}

L.geoJson(t, {
onEachFeature: onEachFeature
}).addTo(map);

4. Using Turf.js, get the sum of the base scenario for two fields.

var sum = turf.sum(p,t,”title”,”titleOutput”);
var sum = turf.sum(p,t,”someOtherProperty”,”otherOutput”);

5. Setup both charts using the base scenario. The example only shows the code for the first chart.

var data={
labels: [“Title Value”],
datasets: [
{label: “Title Value”,
fillColor: “rgba(151,187,205,0.5)”,
strokeColor: “rgba(151,187,205,0.8)”,
highlightFill: “rgba(151,187,205,0.75)”,
highlightStroke: “rgba(151,187,205,1)”,
data: [sum.features[0].properties.titleOutput]}]};

var ctx = document.getElementById(“myChart”).getContext(“2d”);
var myBarChart = new Chart(ctx).Bar(data);

6. Hold the value we will change in a variable so we can change it later.

var x=sum.features[0].properties.titleOutput;

7. Setup an onclick method to modify the scenario. The method will add a marker and provide a textbox to set a value.

map.on(“click”,function(e){
L.marker(e.latlng).addTo(map).bindPopup(‘Add Title Value:<input type=”text” id=”v”><br><button onclick=”add()”>Add</button>’).openPopup();
});

8. Lastly, when the user clicks the button on the new marker, we update the scenario and redraw the chart.

function add(){
myBarChart.destroy();
newValue=Number(document.getElementById(“v”).value);
x+=newValue;
var d = {
labels: [“Title Value”],
datasets: [{
label: “Title Value”,
fillColor: “rgba(151,187,205,0.5)”,
strokeColor: “rgba(151,187,205,0.8)”,
highlightFill: “rgba(151,187,205,0.75)”,
highlightStroke: “rgba(151,187,205,1)”,
data: [x]}]};
myBarChart= new Chart(ctx).Bar(d);
}

If you add a point in the study are, you can add a new Title Value and the chart will update as in the image below.

Title Value is now 10.

Title Value is now 10.

This is a simplistic example but it shows the possibilities for live updating a dashboard as the data changes. This method allows for quick iterations in your models.

Agent Based modeling in ArcGIS with Agent Analyst (RePast)

12 Oct

I love agent based modeling. I have tried to write agents by hand in classes and then use them to call arcpy to get data. Not a smart or efficient way to go about things. Why reinvent the wheel when there are already some great libraries that handle a lot of the work for you. I want my model to integrate with GIS – because I have tons of data already in GIS and because I need a graphical representation of the model and I do not want to program it by hand.

Agent Analyst is the solution.

There is  even a free book with exercises and data. Simple to install and the book is excellent. I skipped ahead a little for my first model and made agents move randomly, write the agent out and refresh the display.  The example will make more sense if you read the book. Here is my code:

In the Agent, the step() function calls self.move()

def move():

coordinate=self.the_geom.coordinate
rand1=Random.uniform.nextIntFromTo(-500,500)
rand2=Random.uniform.nextIntFromTo(-100,300)
coordinate.x=coordinate.x+rand1
coordinate.y=coordinate.y+rand2

The agents just move in a random direction.

In the Model you need the following functions:

def updateDisplay():
self.updateGISDisplay()

def writeAgents():
self.writeAgents(self.myagents,”C:\Users\Me\Desktop\myagent.shp”)

Then in the Environment, you must schedule the writeAgents() then the updateDisplay() functions.

Not the most detailed post, but I have just started playing and wanted to let you know this tool is out there.

Public Safety GIS 2: Python, MongoDB, C# and ArcGIS Desktop Explorer

17 Apr

I wrote a plugin for ArcGIS Desktop explorer that called a python script and passed the map center as an argument. The python script queried a MongoDB and wrote out a csv of closest points. The button then loads the csv as points. I had to do this because I didn’t know how to do it in C#. Well, I figured it out.

Now C# does all the work querying the database and drawing the points. I still have a python script that reads data from a csv export from sharepoint, geocodes it and creates the mongoDB.

Here is my code:

PYTHON TO CREATE THE DB:

from pymongo import Connection,GEO2D
import urllib, urllib2, simplejson
import csv

data = csv.reader(open(‘C:\Documents and Settings…\myfile.csv’))
fields=data.next()
db=Connection().rtcc
db.cnau.create_index([(“loc”,GEO2D)])

for row in data:
try:
address=row[0]
combinedaddress=address+”,albuquerque, NM”

param = {‘q’: combinedaddress,’output’: ‘json’, ‘oe’: ‘utf8’}
url = ‘http://maps.google.com/maps/geo?&#8217; + urllib.urlencode(param)
rawreply = urllib2.urlopen(url).read()
reply = simplejson.loads(rawreply)
print reply[‘Placemark’][0][‘Point’][‘coordinates’][0]
long=reply[‘Placemark’][0][‘Point’][‘coordinates’][0]
lat=reply[‘Placemark’][0][‘Point’][‘coordinates’][1]
z={“loc”:[long,lat],”Activity”:row[1]}

db.cnau.insert(z)
except KeyError:
pass

 

C# TO PLUGIN ARCGIS DESKTOP EXLPORER:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGISExplorer;
using ESRI.ArcGISExplorer.Application;
using ESRI.ArcGISExplorer.Mapping;
using ESRI.ArcGISExplorer.Geometry;
using ESRI.ArcGISExplorer.Data;
using ESRI.ArcGISExplorer.Threading;

using MongoDB;
using MongoDB.Bson;
using MongoDB.Driver.Linq;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace MongoC
{
public class Button : ESRI.ArcGISExplorer.Application.Button
{

public override void OnClick()
{

Folder folderMapItem = new Folder();
MapDisplay mdisp = ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay;

//use this to change the projections 35,-106
FeatureLayer proj = FeatureLayer.OpenShapefile(“C:\\Documents and Settings…\\forProjection.shp”);
// Path for Windows 7: FeatureLayer proj = FeatureLayer.OpenShapefile(“C:\\Users\\user\\…\\forProjection.shp”);
mdisp.CoordinateSystem2D = proj.CoordinateSystem;
string center = mdisp.Center.ToString();
folderMapItem.Name = “Close To Me”;
string[] c = center.Split(null);
String lon = c[3];
String lat = c[6];

MongoClient client = new MongoClient(); // connect to localhost. Deploy at: “mongodb://ip.of.host:27017”
MongoServer server = client.GetServer();
MongoDatabase db = server.GetDatabase(“rtcc”);

var collection = db.GetCollection<BsonDocument>(“cnau”);

var query = Query.Near(“loc”, double.Parse(c[3]), double.Parse(c[6]));
//var cursor = collection.Find(query); Put this in foreach so dont need it anymore.
foreach (BsonDocument item in collection.Find(query).SetLimit(5))
{
BsonElement loc = item.GetElement(“loc”);
string g = loc.Value.ToString();
string x = g.Trim(new Char[] { ‘[‘, ‘]’ });
String[] a = x.Split(‘,’);
// BsonElement name = item.GetElement(“name”); Work for all indexes in MongoDB
// Pass the other data to the notes popups below

ESRI.ArcGISExplorer.Geometry.Point mypoint = new ESRI.ArcGISExplorer.Geometry.Point(double.Parse(a[0]), double.Parse(a[1]));

// Create a Note using the Point geometry.
Note mypointNote = new Note(“MyNote”, mypoint, Symbol.Marker.Pushpin.Orange);

// Populate the Note popup with information about the Point geometry using the ToString method.
StringBuilder info = new StringBuilder(@”<p><a href=””http://google.com””>Google</a></p>&#8221;);
info.AppendLine(@”<p>” + mypoint.ToString() + @”</p>”);

//MapDisplay disp = ESRI.ArcGISExplorer.Application.Application.ActiveMapDisplay;
mypointNote.Popup.Content = info.ToString();

// Add the Note to the current Map.
//mdisp.Map.ChildItems.Add(mypointNote);
folderMapItem.ChildItems.Add(mypointNote);
}

mdisp.Map.ChildItems.Add(folderMapItem);
}
}
}

MongoDB in QGIS

28 Dec
Displaying my MongoDB Data

Displaying my MongoDB Data

I recently installed the MongoDB Plugin for QGIS. The plugin allows you to connect to a MongoDB and load your data. I stored point data – long and lat – for all the public art in Albuquerque. Using the plugin, I connected to my MongoDB and loaded them.

I had trouble getting the plugin to install. My fix was to install pymongo and BSON to my local Python2.7 then copy it from the site-libs to the QGIS directory (C:\Program Files\Quantum GIS Lisboa\apps\Python27\Lib\site-packages). then I had to turn on the plugin under manage plugins.

This did the trick. Then I fired up my mongod, loaded QGIS and connected using the defaults. It prompted me for a Database and then a Collection. Looks like it requires a GEO2D. You can see my data in the image above.

Can’t wait to start playing….

MongoDB and Shapely

18 Nov

I wanted to connect my mongoDB to Shapely so that I could create buffers and do some other things, like spit out my points and buffers as WKT. Here is what I came up with.

from pymongo import Connection,GEO2D
from shapely.geometry import asShape,Point
db=Connection().a
db.b.create_index([(“loc”,GEO2D)])

db.b.insert({“loc”:[-106,35]})
db.b.insert({“loc”:[-109,39]})

for q in db.b.find({“loc”:{“$near”:[-106,35]}}).limit(1):
point=Point(q[“loc”][0],q[“loc”][1])  #this is passing mongoDB to Shapely

point.x    #just checking I didn’t make a mistake

point.y

t=point.buffer(2)
t.area  #checking. Result is 12.a lot of number

point.wkt

t.wkt

Now I can use mongoDB and find all the points in a bounding box or near another point or near a point where type=Coffee Shop, then send that to Shapely for a buffer or to measure distance or whatever, then spit it out as WKT. Saw a tutorial on using shapely with GDAL/OGR and Fiona. Goodle that and see how to send it all back to a shapefile.

Calling Python From PHP: A GIS Example

12 Nov

I showed in a past post how you could use Python and CherrPy to make web based GIS applications. While I think this is a great way to develop, I currently have WAMP installed and can run PHP through Apache without any extra configuration. I decided to take a look at how I could run my Python GIS Scripts through a PHP based website. It turns out that it is a rather simple task:

<?php exec(“C:\python25\python mygi.py”); ?>

That is all there is to it. Now let me give you an example of how I would use this:

  1. I want to create a website where a person can select one of my shapefiles and a toolbox and then have the output sent back to them. If you want you could even let them upload their own file then you run the tools on it – if they wanted the results for their ArcGIS Online maps and lacked processing tools.
  2. For simplicity, I will default to the Gi* toolbox.
  3. When they click submit, I will generate the Python code in the PHP and then run the script. Doing this allows me to customize all of my Python scripts based on user preference. I can plug user input in to all of my functions.
  4. Lastly, the website will hand back the zipped files.

Here is the code for the form.

<html>
<head></head>
<body>
<H1>Gi*</H1>
<H3>Enter a file to run Gi*</H3>
<form name=”input” action=”gi.php” method=”get”>
Shapefile: <input type=”text” name=”shp”><br>
<input type=”submit” value=”Submit”>
</form>
</body>
</html>

Simply a text box to enter a file name and a submit button. The for calls gi.php, which contains:

<html>
<head></head>
<body>
<H1>Running Gi*</H1>
<h2>On file <?php echo $_GET[“shp”];  ?>
</H2>

<?php
$py= fopen(‘mygi.py’, ‘w+’);
$header=’import arcgisscripting’.”\n”.’import zipfile’.”\n”;
$gp = ‘gp = arcgisscripting.create()’.”\n”.’gp.workspace = “C:\wamp\www\output”‘.”\n”;
$hs=’hs = gp.HotSpots(“‘.$_GET[“shp”].'”, “Score”, “HotSpots.shp”, “Fixed Distance Band”, “Euclidean Distance”, “None”, “2500”, “#”, “#”)’.”\n”;
$z=’zipped = zipfile.ZipFile(“C:\wamp\www\output\hotspots.zip”, mode=”w”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.shp”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.shx”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.prj”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.shp.xml”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.sbx”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.sbn”)’.”\n”.’zipped.write(“C:\wamp\www\output\hotspots.dbf”)’.”\n”.’zipped.close()’;
fwrite($py,$header);
fwrite($py,$gp);
fwrite($py,$hs);
fwrite($py,$z);
?>

<H3>Wrote Python….Executing</H3>

<?php exec(“C:\python25\python mygi.py”); ?>

<H3>Completed…..</H3>

<p>
<a href=”/output/HotSpots.zip”>hotspots.zip</a>
</p>

</body>
</html>

The first thing the script does is print the name of the file they entered as an <H2>. Then the PHP opens a python file and writes the code. It then executes the code and returns an HREF to the zipfile. To make the actual Python code clear, I will include it:

import arcgisscripting
import zipfile

gp = arcgisscripting.create()
gp.workspace = “C:\wamp\www\output”
hs = gp.HotSpots(“Input.shp”, “Score”, “HotSpots.shp”, “Fixed Distance Band”, “Euclidean Distance”, “None”, “2500”, “#”, “#”)

zipfile.ZipFile(“C:\wamp\www\output\hotspots.zip”, mode=”w”)
zipped.write(“C:\wamp\www\output\hotspots.shp”)
zipped.write(“C:\wamp\www\output\hotspots.shx”)
zipped.write(“C:\wamp\www\output\hotspots.prj”)
zipped.write(“C:\wamp\www\output\hotspots.shp.xml”)
zipped.write(“C:\wamp\www\output\hotspots.sbx”)
zipped.write(“C:\wamp\www\output\hotspots.sbn”)
zipped.write(“C:\wamp\www\output\hotspots.dbf”)
zipped.close()

I am still on Arc 9.3. That’s why you see the import of arcgisscripting. I believe in 10 it’s arcpy.