This project has moved. For the latest updates, please go here.

how to make a copy of a TileLayer ?

Dec 29, 2015 at 11:55 PM
Edited Dec 29, 2015 at 11:57 PM
I would like to make a copy of a TileLayer.

In public partial class MapBase : MapPanel,
   public TileLayer TileLayer
    {
        get { return (TileLayer)GetValue(TileLayerProperty); }
        set { SetValue(TileLayerProperty, value); }
    }
Is there a clone() that can do this ?
       TileLayer myTileLayer = map.TileLayer.Clone()
When I used
    myTileLayer = map.TileLayer;
    map.Children.Add(myTileLayer);
I got:
    An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll

    Additional information: Specified element is already the logical child of another element. Disconnect it first.
Thanks !
Coordinator
Dec 30, 2015 at 7:23 AM
Edited Dec 30, 2015 at 7:27 AM
Not sure why you would need this, but you have to create a new TileLayer with the same property values as the original one, like
myTileLayer = new TileLayer
{
    TileSource = map.TileLayer.TileSource,
    ...
};
You may also just remove the old TileLayer before adding a new one, so this should also work:
myTileLayer = map.TileLayer;
map.Children.Remove(myTileLayer);
map.Children.Add(myTileLayer);
Dec 30, 2015 at 9:09 PM
Edited Dec 30, 2015 at 9:31 PM
Thanks,

I would like to add a transparent map tile layer with the same properties as map.TileLayer to the map.

transparent means that it has no countries or any other geographic geometries on it,

I need to add some customized data on the transparent layer and then add it on top of the map.TileLayer.

Now, the added customized tileLayer have all images as black even though I have set the image to be transparent.

The customized data can be added to the tileLayer but the geometries (generated from the customized data) are surrounded by these black tile images.

I have set the image opacity to be 0 or 0.2 such as
                Bitmap myImage =CreateNonIndexedImage(aBitmap);
                aBitmap = (Bitmap)SetImageOpacity(myImage , 0.2F); // opacity = 0.2
                BitmapSource bitmapSource =CreateBitmapSourceFromBitmap(aBitmap);
                aTile.SetImage(bitmapSource); // aTile is MapControl Tile
All three functions work well. But, the result is not what I expect.


Did I do something wrong ?

Thanks,
Coordinator
Dec 30, 2015 at 10:40 PM
Edited Dec 30, 2015 at 10:41 PM
It won't work that way, because the Tile.SetImage() method sets the bitmap Opacity to 1. Instead, the bitmaps themselves should be transparent, i.e. have an alpha value of zero or near zero.

Besides that, you should not try to create a modified TileLayer. Instead (as I already told you in this discussion), you should use a derived ImageTileSource that provides custom map tile images.
Dec 31, 2015 at 4:33 AM
Edited Dec 31, 2015 at 4:34 AM
Thanks !

I would like to implement the following in C# code not in XAML because it always gives me error.

My understanding about the code is that the two TileLayers are overlayed by adding "local:MyTileSource" on top of TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png", correct ?

How to implement the overlay by C# ?

thanks!

It was copied from https://xamlmapcontrol.codeplex.com/discussions/647808
 <map:Map>
 <map:Map.TileLayers>
    <map:TileLayerCollection>
        <map:TileLayer
            SourceName="OpenStreetMap"
            TileSource="http://{c}.tile.openstreetmap.org/{z}/{x}/{y}.png"
            MaxZoomLevel="19"/>
        <map:TileLayer>
            <map:TileLayer.TileSource>
                <local:MyTileSource/>
            </map:TileLayer.TileSource>
        </map:TileLayer>
    </map:TileLayerCollection>
 </map:Map.TileLayers>
</map:Map>
Coordinator
Dec 31, 2015 at 7:51 AM
"it always gives me error" is ridiculous. You should try to find out why your XAML isn't working. If you refuse to learn the basics, you will never get things done in WPF and XAML.

Anyway, doing something similar in code behind is as simple as this:
var myTileLayer = new TileLayer
{
    TileSource = new MyTileSource()
};
map.TileLayers.Add(TileLayer.OpenStreetMapTileLayer); // the default TileLayer
map.TileLayers.Add(myTileLayer);
Besides that I forgot to mention that your custom TileSource can return null for completely transparent tile images.
Dec 31, 2015 at 5:21 PM
Edited Jan 1, 2016 at 3:02 PM
Thanks!
I have followed your suggestion but no customized data can be shown on the map.

I have defined a local TileSource like this:
 public class  MyTileSource : ImageTileSource
 {
     public Dictionary<Quadkey, BitmapSource> MytileDict = new Dictionary<Quadkey, BitmapSource>();  
     // this is used to get customized data by quadkey BitmapSource holds the customized Bitmap 
     // corresponding to the tile taged by Quadkey

    public override ImageSource LoadImage(int x, int y, int zoomLevel)
    {
        BitmapSource bm ;
        Quadkey quadkey = new Quadkey(x, y, zoomLevel);
        if (MytileDict.ContainsKey(quadkey))
            bm = MytileDict[quadkey];
        else
            bm = null;
        return bm;
    }
}

 public class Quadkey
{
    public Quadkey(int x, int y, int zoomLevel)
    {
        X = x;
        Y = y;
        ZoomLevel = zoomLevel;
    }

    public int X { get; set; }
    public int Y { get; set; }
    public int ZoomLevel { get; set; }
}
In MainWindow.xaml.cs, I have :
MyTileSource myTileSource = new MyTileSource();
        customTileLayer = new TileLayer
        {
            TileSource = myTileSource // I have tried TileSource = = new MyTileSource(); but, no customized data shown on map. 
        };
        customTileLayer.Description = "MY TILE layer";
        var tileArray = map.TileLayer.Tiles.ToArray(); // get all tiles for current map tile layer. 
        for (int i =0; i < tileArray.Count(); ++i)
        {   
            Quadkey quadkey = new Quadkey(tileArray[i].X, tileArray[i].Y, tileArray[i].ZoomLevel);
            customTileLayer.Tiles.Add(new Tile(quadkey.X, quadkey.Y, quadkey.ZoomLevel));  // no matter I added tiles or not, the results are same, no customized data was shown on the map.
            TileService.ProcessTiles(type, quadkey, tileArray[i], ref myTileSource.tileImageByQuadkeyDict);
        }
        map.TileLayers.Add(customTileLayer);
My idea is: The two layers (one is OSM map layer, another is customTileLayer) should have same tiles even though tile images are different. The customTileLayer is transparent before customized data was added. In MytileDict, I used OSM map layer (the default layer) tiles quadkey as key to associate with an image that has customized data.

But, why in LoadImage(), none of any given X,Y,Zoomlevel canbe found in MytileDict ?

Thanks !
Jan 1, 2016 at 12:20 AM
Now, I can add the customized data to the customTileLayer.

But, the tiles are not updated when I changed the zoom level or did panning.

Currently, my function that add customized tile layer is implemented in MainWindow.xaml.cs.

When a radio button is clicked, the function will be executed. But, I need to run the function (to update tiles) when map is zoomed or panned.

In MapBase.cs, I found :
    public event EventHandler ViewportChanged;
    protected override void OnViewportChanged()
But, I need to collect new tiles when mapport view is changed so that I can update them with the customized data.
If I call the update function from OnViewportChanged() in Mapbase, I cannot access "map" defined in MainWindow.xaml.

How to call the update function when ViewportChanged event is raised from MainWindow.xaml.cs ?

Thanks
Coordinator
Jan 1, 2016 at 10:17 AM
Edited Jan 4, 2016 at 2:29 PM
It doesn't make sense to "collect new tiles" when the map viewport changes. You should do it in the LoadImage method of your custom TileSource. When the map is panned or zoomed, it is called with different x, y, and zoomLevel values.

Anyway, you would use the ViewportChanged event like any other .NET event. Add a handler:
<map:MapBase x:Name="map" ViewportChanged="MapViewportChanged"/>
And implement it in code behind:
private void MapViewportChanged(object sender, EventArgs e)
{
    // access map here, either by the "map" field, or by the sender argument
}
Marked as answer by ClemensF on 1/1/2016 at 3:20 AM