ThinkGeo.com    |     Documentation    |     Premium Support

Integrating Custom Raster Data from an External Source

Hi,


I have a function that returns custom bitmap data (Custom map) and would like to add it to Mapsuite as either a static or dynamic layer.


How can i do that ?


When i tried to load a bitmap images onto the map, it requires the world file which i don't have since it's generated dynamically.


Please help. I would think there must be an easy way to solve this problem.


Thanks!



Kwong, 
  
   Do you know the upper left and lower right coordinates of the images you generate?  I really need to know this part before we give you a sample.  Do you get the extent of the map control then pass it into your other system and then it generates an image the same size as the map control? 
  
 David

David, 
  
 Sorry but i only know the center coordinates at this point. 
  
 Can you provide an example how i should draw bitmap image dynamically without outputting to a file shown above ? 
  
 Thanks!

 
 I would like to clarify again! 
  
 Probably, i can modify my other system to take in upperleft/lowerright coordinates. As i can work out the center point with that. 
  
 I am wondering how can i handle the zoom-in/zoom-out events in the map ?  
 This is crucial because when they zoomed in/out, i can get the current extent (upperleft/lowerright coordinates) dynamically and redraw the world image. 
  
 Not sure currently if Mapsuite support this. If so, please show me an example how i can achieve this. 
  
 Thanks!

Kwong, 
  
   We can work with the center point.  I need to know a little bit more about the image you get from your function.  Do you call this function every time the map redraws?  Does the function give you back a bitmap that fits perfectly on the screen?  I mean does the images height and width match the height and width of the map control?  What parameters do you pass into the function?  Does the function give you an image back that is much larger than the current extent?  The reason I need to know is that there are many ways and types of images you could get back.  If the image that the function passes back is the exact same size as the map control then we can just draw it without worrying about scaling it.  This would be the case if on every map pan, zoom in and out we call the function and it gives us a new map.    A method signature like this would tell me it works that way. GetImage(Point UpperLeft, Point LowerRight, int ScreenWidth, int ScreenHeight), or one that takes a center point, a scale and a height and width.  The more information you can give me about the image you get form your function the better. 
  
 Once I know more about the image you get I can handle the zoom in and zoom out.  If you get a new image when you do this then its a piece of cake.  If not then we need to add some small logic to scale/stretch or shrink the bitmap.  As soon as I know I can whip up the sample.  No matter what we can support this case and I think you will be surprised how easy it is.  We just need all the facts first. :) 
  
 David

Hi David, 
  
 Thanks for the reply, 
  
 Currently, the bitmap given back by my function might need to be scaled. The width/height are not the same in all cases i have tested. For now, the function needs to be recalled when a more detailed bitmap needs to be generated (zoom in/out for now). 
  
 Currently, Below is the Mapsuite’s parameters passed. (This might change) 
 DrawAt(/*Point screenPoint double CentreLatitude, double CentreLongitude,*/ double UpperLeftLatitude, double UpperLeftLongitude, double LowerRightLatitude, double LowerRightLongitude) 
  
 I also need to redraw the bitmap when the user zoom in/out like you said. 
  
 Thanks! 
 

Kwong, 
  
   Thanks for the information, what determines the height and width in screen pixels of the return size of the bitmap?  I mean how does the function know how large of a image to return back?  This will affect the scaling.  I think we are pretty close to having all the answers, just a few critical details and we will have a sample. 
  
 David 
 

David, 



For the height/width issues, I am generating the bitmap based on the size of the current extent (Map control). 

I am wondering if it's possible for Mapsuite to provide an API to determine the size (width/height) of user's browser. This can be done via javascript but would be need if integrated with Mapsuite. 



Also, i might need to redraw the bitmap when users pan (all direction). Would these events (zoom in/out, pan left/right/etc) be provided ? 



Thanks!






 


Kwong,


I have put together the code for you new custom layer.  The first part is how to use it and the second part is the actually new class.  


            // Here I create my custom layer

            SimpleCustomLayer customLayer = new SimpleCustomLayer();



            // Here I add my cusotm layer to the layers collection.

            Map1.StaticLayers.Add("CustomLayer", customLayer);




_+_+_+_+_+


New Class


_+_+_+_+_+


 


using System;

using System.Collections.Generic;

using System.Text;

using System.Drawing;



// What this sample does is creates your own layer.  You can use this layer just like any other layers 

// in the system.  The advantage is that you can place it where you want and it will seamlessly integrate

// with the rest of the system.  As you can see it is really easy to create your own layers.



// The basic flow of events is that when the map needs to draw it will first call the OpenCore.

// This is where you do anything special to get your source ready.  Then it will call the GetBoundingBox

// method to make sure the current extent of the map is within the are you have data.  If not then it will

// never call the DrawCore because there is no need.  Then if you are int he extent it wil call the DrawCore.

// The DrawCore is where you place your code that need to do the drawing on the canvas.  As you can see below

// it is faily simple.



namespace ThinkGeo.MapSuite.Core

{

    class SimpleCustomLayer : Layer

    {

        protected override void OpenCore()

        {

            //  Here you should do anything you need to do it initilize your source.

            //  Maybe for you it is nothing and if so you can leave it blank.

        }



        protected override void CloseCore()

        {

            //  Here you should do anything you need to do it close your source.

            //  Maybe for you it is nothing and if so you can leave it blank.

        }



        protected override RectangleShape GetBoundingBoxCore()

        {

            // Here you will want to pass back a rectangle that defines the entire extent of

            // of image source.  So if a user pans outside off of where you have imagry for the map

            // control we will not try and fetch it.  If your data is for the whole world or you are

            // fine to just passing back a transparent image if the user goes off the edge then you can use

            // the default code I added.



            //This assumes that your map is in decimal degrees and covers the whole world, if not you need to know the bounds.

            return new RectangleShape(-180.0, 83.0, 180.0, -90.0);

        }



        protected override void DrawCore(GeoCanvas canvas, GeographyUnit mapUnit, System.Collections.ObjectModel.Collection<SimpleCandidate> labeledInLayers)

        {

            // Here is where you will call your API.  You will get a bitmap back and then draw it in the canvas.

            // I have provided a mock method call to simulate what you will do.



            Bitmap customImage = GetImage(canvas.CurrentWorldExtent.UpperLeftPoint.X, canvas.CurrentWorldExtent.UpperLeftPoint.Y, canvas.CurrentWorldExtent.LowerRightPoint.X, canvas.CurrentWorldExtent.LowerRightPoint.Y, (int)canvas.CanvasWidth, (int)canvas.CanvasHeight);



            // Here we draw the image you generated to the canvas.

            canvas.DrawWorldImageWithoutScaling(canvas.ToGeoImage(customImage), canvas.CurrentWorldExtent.GetCenterPoint().X, canvas.CurrentWorldExtent.GetCenterPoint().Y, DrawingLevel.LevelOne);



            customImage.Dispose();

        }



        private Bitmap GetImage(double upperLeftX, double upperLeftY, double lowerRightX, double lowerRightY, int screenWidth, int screenHeight)

        {

            // In here you would get your image and return it.

            // I am just returning an image with some text so you can verify it works..



            Bitmap customImage = new Bitmap(screenWidth, screenHeight);

            Graphics g = Graphics.FromImage(customImage);

            g.DrawString("SimpleCustomLayer Test", new Font("Arial", 20, FontStyle.Bold), Brushes.Black, new PointF(screenWidth / 2, screenHeight / 2));

            g.Dispose();



            return customImage;

        }



    }

}

 


David




 
 How should i handle the pan/zoom events on the Map ? 
 Also what’s the proper way of including the dynamic bitmap ? Is there a sample around ?  
 Currently, i am writng bitmap to a file and reading from it using the method below. 
  
  GdiPlusImageLayer ecwImageLayer = new GdiPlusImageLayer(@“c:\test.png”) 
                                                       { 
                                                           UpperThreshold = double.MaxValue, 
                                                           LowerThreshold = 0 
                                                       }; 
                  
                 Map1.StaticLayers.Add(ecwImageLayer); 
  
  
  How can i apply the GeoCanvas class for my case ? 
  
 Thanks!

Kwong, 
  
 I have included a sample custom layer above.  It should have everything you need.  You do not need to save the image to disk if your method returns a bitmap class.  Also is this image from an ECW file?  If so we have an ECWLayer that you can use to access ECW images. 
  
 David 
 

Thanks. 
  
 No, it should be a ECW file. 
 I will be trying out the new layer.  
 Will be posting again if i encounter any more problems.

Did the ECWLayer work for you?  Let us know if you run into any problems. 
  
 Thanks!

The custom layer by David is working but would be better if dynamic layer support tiling as well. 
 

Thanks! We will put this on the list for possible enhancments.

Forum Users,


  With some API changes from the beta here is an updated sample if anyone runs into this in the future.


David


 




    public class CustomLayer : Layer
    {
        protected override void DrawCore(GeoCanvas canvas, Collection<SimpleCandidate> labelsInAllLayers)
        {
            double scale = ExtentHelper.GetScale(canvas.CurrentWorldExtent, canvas.Width, canvas.MapUnit);
            // Here you set the scales at which you want to draw.  
            // If you also know the bounding box of your data set then I
            // suggest you uncomment our the last two methods and change the 
            // sample bounidng box I provided.  If you do not know then leave them
            // commented or remove them
            if (scale >= 0 && scale <= 1000000)
            {
                Bitmap customImage = GetImage(canvas.CurrentWorldExtent.UpperLeftPoint.X, canvas.CurrentWorldExtent.UpperLeftPoint.Y, canvas.CurrentWorldExtent.LowerRightPoint.X, canvas.CurrentWorldExtent.LowerRightPoint.Y, (int)canvas.Width, (int)canvas.Height);

                // Here we draw the image you generated to the canvas.
                canvas.DrawWorldImageWithoutScaling(canvas.ToGeoImage(customImage), canvas.CurrentWorldExtent.GetCenterPoint().X, canvas.CurrentWorldExtent.GetCenterPoint().Y, DrawingLevel.LevelOne);
                customImage.Dispose();

                // You can also use any one of these kinds fo draw depending on if you
                // have world or screen coordinates and if you need scaling or
                // not.
                //canvas.DrawScreenImage();
                //canvas.DrawScreenImageWithoutScaling();
                //canvas.DrawWorldImage();
                //canvas.DrawWorldImageWithoutScaling();                
            }
        }

        private Bitmap GetImage(double upperLeftX, double upperLeftY, double lowerRightX, double lowerRightY, int screenWidth, int screenHeight)
        {
            // In here you would get your image and return it.
            // I am just returning an image with some text so you can verify it works..

            Bitmap customImage = new Bitmap(screenWidth, screenHeight);
            Graphics g = Graphics.FromImage(customImage);
            g.DrawString("SimpleCustomLayer Test", new Font("Arial", 20, FontStyle.Bold), Brushes.Black, new PointF(screenWidth / 2, screenHeight / 2));
            g.Dispose();

            return customImage;
        }

        //// If you know the bounding box then you need
        //// set this to true as I have.  If you do not know
        //// then leave all of this commented out.
        //public override bool HasBoundingBox
        //{
        //    get
        //    {
        //        return true;
        //    }
        //}

        //// Here is where you would return what the extent of you data is 
        //// in whatever coordinate system it is.  I entered som decimal degree
        //// numbers but that is just a sample.
        //protected override RectangleShape GetBoundingBoxCore()
        //{
        //    return new RectangleShape(-180.0, 83.0, 180.0, -90.0);           
        //}
    }



Hi,


I have my map data on a centralized server and it returns maps in the format of bitmap to my desktop application.  I used the code above and it works seamlessly as if the map data is available locally.  I can zoom and pan.  However, my initial map is always smaller than the map control.  The canvas size is 1024X512 while my map control is 1280X968.  How can I set the canvas size so that it matches the size of my map control when the map is first loaded? 


Here is my code:



        private void Form1_Load(object sender, EventArgs e)
        {
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean);

            LayerOverlay staticOverlay = new LayerOverlay();
            SimpleCustomLayer customLayer = new SimpleCustomLayer();
            staticOverlay.Layers.Add("CustomeLayer", customLayer);
            winformsMap1.Overlays.Add(staticOverlay);

            winformsMap1.CurrentExtent = new RectangleShape(-143.4, 109.3, 116.7, -76.3);
            winformsMap1.Refresh();
        }


Also, I tried to set the bounds so that I can display a smaller map than the whole world.  However, it still displays the whole world since the canvas.CurrentWorldExtent always contains the values (-180, 90, 180, -90) 


 



       protected override RectangleShape GetBoundingBoxCore()
        {
            return new RectangleShape(-143.4, 109.3, 116.7, -76.3);
        }



Thanks!

 



Tracy, 
  
 Honestly speaking, this is some advanced topic; hopefully I can make things clear to you. 
  
 All the behaviors are related with the “snapped” extent which will change based on the input Current Extent and Current Scale. Then why we do this? Because the Desktop Edition is build based on the tile-based cache system, so every time you set a extent for the MapControl, what we will draw is not only the area of this extent, but another “enlarged” extent to fit exactly the cache grid system. 
  
 Any all, I think both extents (CurrentExtent and CanvasExtent) exposed in the  DrawCore as well as the CanvasWidth and CanvasHeight(mapWidth and mapHeight) can give you very concrete position and full control. 
  
 Just a reminder, this is DesktopEditon problem, just create a post in the DestkopEdition forum~! 
  
 If you have any other problem or if I am not clear enough just let me know. 
  
 Thanks. 
 Yale