Pages

Thursday, March 21, 2013

Esri DevSummit 2013 Sessions Explorer


open application

I decided not to make my DevSummit Sessions Explorer this year but changed my mind when I saw there is a contest. ;-)

My JavaScript skills are limited so It took me around 20 hours to complete it. With Silverlight it would have taken me 3-4 hours tops.

This app (88 sloc) contains :
  • Filtering the 200 sessions by name, room and day/time (i.e. : 'javasc', 'wed 08', 'smoketr').
  • Filtering by pressing the room over the map.
  • Highlighting the room when you mouseover the list.
  • Expand each session from Esri developer summit site (not as simple as it sounds) so you can see the session full details. Used PHP external proxy page for this.
  • Pan to expanded session (try the Hilton sessions).
Libraries:
  • Esri JavaScript API
  • Dojo
  • JQuery
Please retweet if you like this application!

Thursday, July 5, 2012

Measuring Over Oblique Images


Image 1


Idan Computers Ltd. released Oblivision for browser application.
The install is quite simple, just point to a virtual directory containing the orthophoto and the oblique images and you are all set. If you add more oblique images in the future, the software will automatically detect them and add the set to the system.


Image 2
So what can you do with this app?
1. Locate the best 4 oblique images at a certain point (click on the orthophoto, search address or search coordinates).
2. Measure distances and areas on the orthophoto and over the oblique images directly!
3. Calculate x/y/z of any point that can be seen in two or more images.
4. Load background layers (i.e. shape files, Esri ArcGIS Server layers) into the orthophoto and the obliques.
5. Sync between all 5 windows.
6. Navigate through all oblique images that are relevant to the specific location.
7. New : identify and show map tips on all 5 windows.
8. New : measure area of a flat / slanted roof and get x,y,z of each corner.
9. New : a time slider which helps to find changes through time.

Image 3

Image 1 : Three height  measurements of the same building.
Image 2 : A precise measurement of a slanted roof.
Image 3 : Showing vector data over the oblique photo.

As for technology, there are two versions for this app:
a. runs over your Esri ArcGIS Server and show/search layers from it. Based both on Esri Silverlight or JS API. Currently the Silverlight app is much more beautiful!
b. No 3rd party software - pure Silverlight and JS.

Feel free to contact me for more details.
Oren.


Sunday, May 6, 2012

Running Borland C++ from Silverlight

I had some massive and complex algorithms written in Borland c++ and wanted to use them in my Silverlight app. Because I know to read c++ but unfortunately don't understand  it, I asked a friend to make a c++ exe which get string parameters and write the result to a file. The second step was to write a simple web service which runs the exe with the parameters and read the output file in the end of the process. The third and last step was to consume the web service from the Silverlight application.

It may sound to some of you as a workaround typical to ten years ago, But most of you are going to write Java Script in the next few years, so don't say a word about that. ;-)

Multiple connections - of course one of the parameters is the output file name so two or more processes can run in the same time.

Security - this is the fun part. The algorithm is inside exe on a server so reading the XAP would not give anything. Even if you get the exe I don't think you can dig up the code (again, not a c++ person).

Although I use WaitForExit in the webserice, It's still don't hang the application because the whole webserive is running async. 

Conclusion : Don't use it unless you have to. It saved me a lot of time in a particular project!


Silverlight Code:
private void m_btn_go_Click(object sender, RoutedEventArgs e)
{
    ServiceReference1ServiceSoapClient c = new ServiceReference1ServiceSoapClient();
    c.get_squareCompleted += new EventHandler<ServiceReference1.get_squareCompletedEventArgs>(c_get_squareCompleted);
    c.get_squareAsync(m_txt_input.Text);
}


void c_get_squareCompleted(object sender, ServiceReference1get_squareCompletedEventArgs e)
{
    MessageBox.Show(e.Result);
}


WebSerice Code:
[WebMethod]
public string get_square(string sInput)
{
    string exeFilePath = "D:\\Path\\Prog.exe";

    System.DiagnosticsProcessStartInfo psi = new System.DiagnosticsProcessStartInfo(exeFilePath);
    psi.UseShellExecute = false; 
    psi.RedirectStandardOutput = true;
    psi.RedirectStandardInput = true;
    psi.RedirectStandardError = true;
    psi.Arguments = sInput;
    psi.WorkingDirectory = "D:\\Path";

    System.DiagnosticsProcess proc = System.Diagnostics.Process.Start(psi);
    proc.WaitForExit(3000);
    proc.Close();
               
    StreamReader pStreamReader = new StreamReader("D:\\Path\\output.txt");
    string pData = pStreamReader.ReadToEnd();
    pStreamReader.Close();

    return pData;
}

Wednesday, February 29, 2012

Convert coordinates from Web Mercator to UTM

Live Sample
Source Code

Wanted to show UTM coordinates (plus zone and hemisphere) while moving the mouse on a Web Mercator map.
Searched and found this very useful class and used it in the MouseMove event of the map.
With Esri Silverlight API it took only a few minutes to do! Used WGS84 ellipsoid.


private void MyMap_MouseMove(object sender, System.Windows.InputMouseEventArgs e)
{
    //Web Mercator
    Point screenPoint = e.GetPosition(MyMap);
    MapPoint pMapPointWM = MyMap.ScreenToMap(screenPoint);
    double x1 = Math.Round(pMapPointWM.X, 0);
    double y1 = Math.Round(pMapPointWM.Y, 0);
    m_txt32.Text = x1 + "," + y1;

    //Geog
    MapPoint pMapPointGeog = (MapPoint)wb.ToGeographic(pMapPointWM);
    m_txt22.Text = String.Format("{0:0.0000},{1:0.0000}", pMapPointGeog.X, pMapPointGeog.Y);

    //UTM
    GeoUTMConverter g = new GeoUTMConverter();
    g.ToUTM(pMapPointGeog.Y, pMapPointGeog.X);
    double x3 = Math.Round(g.X);
    double y3 = Math.Round(g.Y);
    m_txt11.Text = "UTM" + g.Zone + g.Hemi.ToString().Substring(0, 1) + "/WGS84";
    m_txt12.Text = x3 + "," + y3;
}

Wednesday, December 14, 2011

Custom Magnifier Additions

Live Sample
Source Code

I know it's been a while since the last post. I'm sorry. A lot of work plus Microsoft continuing mistakes regarding future technologies kept me busy.

In the past year I thought that the end of Silverlight will be sealed when Dave Campbell will changed his website name. And he did finally. Therefore I have changed my Blog name also but it's not final. I don't feel  the name describes the content of the blog perfectly.

But we have two or three years to develop and maintain our SL apps so this post will show you how easily you can enjoy your mouse wheel in a GIS application written in Silverlight (Esri API).

I took the custom magifier (by Richie Carmichael) and added these lines to the class:


public Magnify()
{
    InitializeComponent();
    this.MouseLeftButtonDown += this.MagnifyBox_MouseLeftButtonDown;
    this.MouseLeftButtonUp += this.MagnifyBox_MouseLeftButtonUp;
    this.MouseMove += this.MagnifyBox_MouseMove;
    this.MouseWheel += new MouseWheelEventHandler(Magnify_MouseWheel);
}

private void m_sld_opacity_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    this.Opacity = e.NewValue;
}

void Magnify_MouseWheel(object sender, MouseWheelEventArgs e)
{
    if (e.Delta > 0 && this.ZoomFactor < 10)
        this.ZoomFactor = this.ZoomFactor + 0.5;
    if (e.Delta < 0 && this.ZoomFactor > 1.0)
        this.ZoomFactor = this.ZoomFactor - 0.5;

    m_txt_zoom_factor.Text = "X" + this.ZoomFactor;
    if (this.ZoomFactor % 1 == 0) m_txt_zoom_factor.Text += ".0";

    m_sld_opacity.Visibility = (this.ZoomFactor == 1 ? Visibility.Visible : Visibility.Collapsed);
    if (this.ZoomFactor > 1) m_sld_opacity.Value = 1.0;
}

Also added two objects in the end of the XAML:
 ...
    <Border Background="Black" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,12,0,0">
            <TextBlock x:Name="m_txt_zoom_factor" Text="X1.0" FontSize="12" Foreground="Yellow" />
        </Border>
        <Slider x:Name="m_sld_opacity" HorizontalAlignment="Center" VerticalAlignment="Bottom" 
                Margin="0,0,0,12" Value="1.0" Maximum="1.0" Minimum="0.2" Width="50" ValueChanged="m_sld_opacity_ValueChanged" />
</Grid>

The ability to zoom in and out with the mousewheel is very useful. If you can add more data to the magnifier and compare - it's useful even more.



Friday, September 9, 2011

Convert coordinates from ECEF to Geographic

Needed to write a Silverlight application which gets ECEF (Earth-Centered Earth-Fixed) coordinates and show the objects on a Web Mercator map in real time. So I Googled a bit and found a lot of pages but none in C#.

Wrote this handy c# function :


public static void convert_ecef_to_wgs84(double x, double y, double z, out double lon, out double lat, out double alt)
{
    lon = 0;
    lat = 0;
    alt = 0;

    double a = 6378137;
    double e = 8.1819190842622e-2;
    double b = Math.Sqrt(Math.Pow(a, 2) * (1 - Math.Pow(e, 2)));
    double ep = Math.Sqrt((Math.Pow(a, 2) - Math.Pow(b, 2)) / Math.Pow(b, 2));
    double p = Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2));
    double th = Math.Atan2(a * z, b * p);
    lon = Math.Atan2(y, x);
    lat = Math.Atan2((z + Math.Pow(ep, 2) * b * Math.Pow(Math.Sin(th), 3)), (p - Math.Pow(e, 2) * a * Math.Pow(Math.Cos(th), 3)));
    double N = a / (Math.Sqrt(1 - Math.Pow(e, 2) * Math.Pow(Math.Sin(lat), 2)));
    alt = p / Math.Cos(lat) - N;

    lon = lon % (2 * Math.PI);

    bool k = Math.Abs(x) < 1.0 && Math.Abs(y) < 1.0;
    if (k)
        alt = Math.Abs(z) - b;

    lon = lon * 180 / Math.PI;
    lat = lat * 180 / Math.PI;
}
  • 'a' and 'e' are WGS84 parameters. You can use any geodetic system.
  • Use this post to convert from lat/lon to Web Mercator.
  • Sources : microemsatsleuth

Friday, September 2, 2011

Showing ArcGIS Dynamic Layer Maptips

Live Sample
Source Code

ArcGIS Server Dynamic Layer returns an image to the client. The image is actually a raster combination of all layers in a specific extent. The service itself is generated from MXD or MSD file created and published on the server.

Because we deal with image, we can't use mouse events like MouseEnter and MouseLeave, but we can use the MouseMove event and "measure" the speed of the mouse movement. Usually when we want to show a Maptip, we move the mouse very slowly over the object that we want to inspect.

So the concept is simple : measure the mouse speed, and when it's greater than zero but still slow - query the server, take the results and show it on the map.


private Point _last_point = new Point(-10, -10);
private DateTime _last_time = DateTime.Now;

private void m_map_MouseMove(object sender, System.Windows.InputMouseEventArgs e)
{
    TimeSpan ts = DateTime.Now.Subtract(_last_time);
    if (ts.Milliseconds < 100)
        return;
            
    Point pCurPoint = e.GetPosition(m_map);
    double dx = Math.Abs(pCurPoint.X - _last_point.X);
    double dy = Math.Abs(pCurPoint.Y - _last_point.Y);
            
    if (dx+dy < 3)
        show_map_tip();
    else
        hide_map_tip();

    _last_point = pCurPoint;
    _last_time = DateTime.Now;
}

private void show_map_tip()
{
    if (_layer_number == -1)
        return;
            
    MapPoint pMapPoint = m_map.ScreenToMap(_last_point);
    double d = m_map.Extent.Width/400;
    Envelope pEnv = new Envelope(pMapPoint.X - d, pMapPoint.Y - d, pMapPoint.X + d, pMapPoint.Y + d);
    pEnv.SpatialReference = new SpatialReference(102100);

    Query pQuery = new Query();
    pQuery.Geometry = pEnv;
    pQuery.OutFields.Add("*");

    string pURL = m_dynamic.Url + "/" + _layer_number;
    QueryTask pQueryTask = new QueryTask(pURL);
    pQueryTask.ExecuteCompleted += QueryTask_Identify_ExecuteCompleted;
    pQueryTask.Failed += pQueryTask_Identify_Failed;
    pQueryTask.ExecuteAsync(pQuery);
}
        
private void QueryTask_Identify_ExecuteCompleted(object sender, QueryEventArgs e)
{
    FeatureSet pFeatureSet = e.FeatureSet;
    if (pFeatureSet == null) return;
    if (pFeatureSet.Count() < 1) return;
            
    string p = "";

    Graphic g = pFeatureSet.Features[0];
    for (int i = 0; i < g.Attributes.Keys.Count; i++)
    {
        string pFieldName = g.Attributes.Keys.ElementAt(i);
        string pVal = g.Attributes.Values.ElementAt(i).ToString();
        if (p.Length > 0)
            p = p + "\n";
        p = p + pFieldName + " - " + pVal;
        if (i > 20)
            break;
    }            
            
    m_txt_maptip.Text = p;
    m_maptip.Visibility = Visibility.Visible;
    m_maptip.Margin = new Thickness(_last_point.X + 10, _last_point.Y + 10, 0, 0);
}

private void pQueryTask_Identify_Failed(object sender, TaskFailedEventArgs args)
{
    hide_map_tip();
}

private void hide_map_tip()
{
    m_maptip.Visibility = Visibility.Collapsed;
}

We can play with the parameters to get better results. This is a first draft and didn't make a lot of tests.
Have fun!