To put it succinctly: you'll need to burn the ArcGIS Viewer for Flex into a thick client (Adobe AIR) implementation in order to take advantage of the Flex/AIR NativeProcess
API, which will allow you to call local executables such as a Python file.
Here's a quick demo app I whipped up awhile back demonstrating how to call an ogr2ogr instruction from a Flex/AIR application. This shows calling a .exe
executable, but calling a local Python script uses the same plumbing.
[The Caveat]
Foremost, while it's impossible to do this from a Flex appelet--that is, a Flex app embedded in an HTML page, it is possible to do this from an Adobe AIR app--that is, a thick-client (the AIR app) implementation that is crippled without its service tier (the ArcServer instance). Consider Google Earth: you install it and run it locally, but it needs your internet connection to pull in the Google data. This is the same thinking.
Also, this example demonstrates only the Flex/AIR implementation of the NativeProcess
API. Adding the ArcGIS Viewer for Flex to the app is irrelevant to the actual problem here, which is merely bridging Flex and Python, or whatever else you want to execute locally.
Additionally, this example is relative to Flex 3 using the Flex 3.6 SDK overlaid with the AIR 3.0 SDK. However, compiling a Flex 3 + AIR solution in Flex Builder 3 will likely prove challenging, as you'll need to compile a native installer (.exe
) manually using the SDK's command line compiler, ADT.
The approach is actually much easier for Flex 4 + AIR solution, presuming you overlay your Flex SDK with a minimum AIR version 2.0 SDK, because Flash Builder 4 can compile the requisite native installer right out of the box.
[Now, the Solution]
1) Make sure to update the supportedProfiles
tag in your AIR app's XML descriptor file so that your application will be compiled with extendedDesktop
support.
<!-- We recommend omitting the supportedProfiles element, -->
<!-- which in turn permits your application to be deployed to all -->
<!-- devices supported by AIR. If you wish to restrict deployment -->
<!-- (i.e., to only mobile devices) then add this element and list -->
<!-- only the profiles which your application does support. -->
<supportedProfiles>extendedDesktop</supportedProfiles>
2) Here's the one-file MXML application I wrote to test this sort of functionality:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical" verticalScrollPolicy="off" height="450"
paddingTop="12" paddingBottom="12"
paddingLeft="12" paddingRight="12"
horizontalAlign="left">
<mx:Script>
<![CDATA[
import __AS3__.vec.Vector;
import flash.desktop.NativeProcess;
import flash.desktop.NativeProcessStartupInfo;
import flash.filesystem.File;
import flash.system.Capabilities;
import mx.controls.Alert;
/**
* Build and execute an OGR2OGR method call from an AIR app.
*
* This app attempts to execute the following OGR2OGR script:
*
* ogr2ogr -f ESRI Shapefile E:\4_GIS\01_tutorials\junk\us_states.shp
* MySql:dbName,host=localhost,user=root,password=pass,port=3306
* us_states
**/
private var _proc:NativeProcess;
private function launchApp():void
{
if(NativeProcess.isSupported)
{
if(Capabilities.os.toLowerCase().indexOf("win") > -1)
{
// Attach to ogr2ogr.exe here..
var file:File = new File(exePathTI.text);
var npsi:NativeProcessStartupInfo;
npsi = new NativeProcessStartupInfo();
npsi.executable = file;
// ogr2ogr method arguments here..
// CAVEAT: ..something doesn't like "quotes" in the OGR2OGR arguments.
// For instance, use ESRI Shapefile, not "ESRI Shapefile".
var procArgs:Vector.<String> = new Vector.<String>();
// output format flag
// -f
procArgs[0] = arg0TI.text;
// output format
// ESRI Shapefile
procArgs[1] = arg1TI.text;
// file destination
// i.e. E:\4_GIS\01_tutorials\junk\us_states.shp
var savePathFile:File = new File(arg2TI.text);
procArgs[2] = savePathFile.nativePath;
// connection string
// i.e. MySql:dbName,host=localhost,user=root,password=pass,port=3306
procArgs[3] = arg3TI.text;
// table name
// i.e. us_states
procArgs[4] = arg4TI.text;
npsi.arguments = procArgs;
_proc = new NativeProcess();
_proc.start(npsi);
_proc.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
_proc.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
_proc.addEventListener(NativeProcessExitEvent.EXIT, onExit);
_proc.addEventListener(IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
_proc.addEventListener(IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
}
else
{
Alert.show("Something bombed..");
}
}
else
{
Alert.show("NativeProcess support not detected");
}
}
private function onOutputData(event:ProgressEvent):void
{
trace( "(Response) THE PROCESS RESPONDED: ",
_proc.standardOutput.readUTFBytes(
_proc.standardOutput.bytesAvailable) );
}
private function onErrorData(event:ProgressEvent):void
{
trace( "(Error) SOMETHING FAILED: ",
_proc.standardError.readUTFBytes(
_proc.standardError.bytesAvailable) );
}
private function onExit(event:NativeProcessExitEvent):void
{
trace( "(Exit) THE PROCESS RETURNED: ", event.exitCode );
}
private function onIOError(event:IOErrorEvent):void
{
trace(event.toString());
}
]]>
</mx:Script>
<!-- full path to the ogr2ogr.exe executable -->
<mx:Label text="Path to the ogr2ogr executable:"/>
<mx:TextInput id="exePathTI" text="C:\Program Files\GDAL\ogr2ogr.exe" width="100%"/>
<mx:Spacer height="25"/>
<!-- sample ogr2ogr arguments that worked in testing -->
<mx:Label text="Output format flag:"/>
<mx:TextInput id="arg0TI" text="-f" width="100%"/>
<mx:Label text="Output format:"/>
<mx:TextInput id="arg1TI" text="ESRI Shapefile" width="100%"/>
<mx:Label text="Output directory (MUST ALREADY EXIST):"/>
<mx:TextInput id="arg2TI" text="E:\4_GIS\01_tutorials\junk\us_states.shp" width="100%"/>
<mx:Label text="OGR Connection string:"/>
<mx:TextInput id="arg3TI" text='MySql:dbName,host=localhost,user=root,password=pass,port=3306' width="100%"/>
<mx:Label text="Table name:"/>
<mx:TextInput id="arg4TI" text="us_states" width="100%"/>
<mx:Button label="Try It!" click="launchApp();"/>
</mx:WindowedApplication>
[The ADT Script]
(Not necessary if using Flash Builder 4, as you can compile a native installer right out of the box!)
I used the following ADT script to compile this for my Flex 3 + AIR 3 implementation:
adt -package -storetype pkcs12 -keystore "C:\dev\flexwksp\CallOgrFromAIR\cert_pass_is_heyflex.p12" -target native -CallOgrFromAIR.exe CallOgrFromAIR-app.xml CallOgrFromAIR.swf
Finally, if you want another example of this kind of thing --one flavored more for Flex 4 and Flash Builder 4, I posted a similar answer on StackOverflow once upon a time. It has a couple supporting screenshots that might also prove helpful.