ThinkGeo.com    |     Blog    |     Wiki    |     Support

API KEY in WMTS

Hi,

I’m hoping this will be an easy question. I would like to access a WMTS service but it needs an API key.

If I add the key as part of the URI like:
wmtsLayer.ServerUris.Add(new Uri(“https://osdatahubapi.os.uk/omse/wmts?key=XXXXXXXXXX”));
Then the send request comes out slightly wrong (has two ? chars)
https://osdatahubapi.os.uk/omse/wmts?key=XXXXXXXXXX?Service=WMTS&Request=GetCapabilities

However if I try to add as a parameter instead like:
wmtsLayer.ServerUris.Add(new Uri(“https://osdatahubapi.os.uk/omse/wmts”));
wmtsLayer.Parameters.Add(“key”, “XXXXXXXXXX”);
Then the send request does not include the key
https://osdatahubapi.os.uk/omse/wmts?Service=WMTS&Request=GetCapabilities

So I was wondering what is the correct way to add the key?

Thanks,
Jonathan
P.S. I will have to do something very similar for a WFS layer so would the same method apply? Also a WFS example would be very helpful - does one exist?

Hi Jonathan,

It looks the key is not included in the standard, so if you want to append it, I think you can do it like this:

            WmtsLayer wmtsLayer = new WmtsLayer();
            wmtsLayer.ServerUris.Add(new Uri("https://osdatahubapi.os.uk/omse/wmts"));
            wmtsLayer.SendingWebRequest += WmtsLayer_SendingWebRequest;

    private void WmtsLayer_SendingWebRequest(object sender, SendingWebRequestEventArgs e)
    {
        e.WebRequest = HttpWebRequest.Create(e.WebRequest.RequestUri.AbsoluteUri + "&key=XXXXXXXXXX");
    }

You should want to modify the logic in SendingWebRequest event to make it works better.

Wish that’s helpful.

Regards,

Ethan

Ethan,

Thanks, the WMTS is working perfectly now.

Do you have any information or examples on WFS Layers you could point me at?

Regards,
Jonathan

Hi Jonathan,

We don’t have a sample for WFS, because it’s very simple to use it.

Here is a sample code about how to render it:

WfsFeatureLayer layer = new WfsFeatureLayer("https://OUR_URL/geoserver/wfs", "nhp:windprobs_view");
layer.open();
layer.GetBoundingBox();
Collection<FeatureSourceColumn> columns = layer.FeatureSource.GetColumns();
Collection<Feature> features = layer.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns);

And here is the package for the layer to render WFS: https://www.nuget.org/packages/ThinkGeo.MapSuite.Layers.Wfs/11.0.0-beta006

Wish that’s helpful.

Regards,

Ethan

Ethan,

Somehow missed your reply, so a slightly delayed thanks.

I am, unfortunately, still getting some problems with setting up to use the WFS.

The code I used to set it up looks like:

using System;
using System.Net;
using System.Windows;
using ThinkGeo.MapSuite;
using ThinkGeo.MapSuite.Drawing;
using ThinkGeo.MapSuite.Layers;
using ThinkGeo.MapSuite.Shapes;
using ThinkGeo.MapSuite.Wpf;

namespace WmtsLayer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {

            // Basic MAP and Overlay settings
            map.MapUnit = GeographyUnit.Meter;
            map.CurrentExtent = new RectangleShape(299575, 188357, 299952, 188022);
            LayerOverlay layerOverlay = new LayerOverlay();
            map.Overlays.Add(layerOverlay);

            // OS need TLS 1.2
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            // WFS Settings
            var wfsLayer = ConfigureWFS();

             // Add WMS or EFS to overlay
            wfsLayer.Open();
            layerOverlay.Layers.Add(wfsLayer);

            var area = ThinkGeo.MapSuite.Styles.AreaStyles.CreateSimpleAreaStyle(GeoColor.SimpleColors.DarkBlue);
            var line = ThinkGeo.MapSuite.Styles.LineStyles.CreateSimpleLineStyle(GeoColor.SimpleColors.BrightBlue, 3, true);
            var point = ThinkGeo.MapSuite.Styles.PointStyles.CreateSimplePointStyle(ThinkGeo.MapSuite.Styles.PointSymbolType.Circle, GeoColor.SimpleColors.BrightRed, 10);

            wfsLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = area;
            wfsLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle = line;
            wfsLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = point;

            wfsLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            
        }


        private WfsFeatureLayer ConfigureWFS()
        {
            WfsFeatureLayer wfsLayer = new WfsFeatureLayer("https://osdatahubapi.os.uk/omse/wfs", "osme:TopographicArea");
            wfsLayer.SendingWebRequest += Layer_SendingWebRequest;
            return wfsLayer;
        }

        private void Layer_SendingWebRequest(object sender, SendingWebRequestEventArgs e)
        {
            e.WebRequest = System.Net.HttpWebRequest.Create(e.WebRequest.RequestUri.AbsoluteUri + "&key=XXXXXXXX");
        }
    }
}

The generated request looks like:

https://osdatahubapi.os.uk/omse/wfs?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=osme:TopographicArea&BBOX=-36175.1191613734,120367.523233153,181516.992918394,338059.635312921&propertyname=SHAPE&key=XXXXXXXX

This does return some data, as confirmed by entering in a browser (copy uploaded)
wfs.xml (107.0 KB)
but also generates the following exception:

  System.Xml.XmlException
  HResult=0x80131940
  Message=ReadElementContentAs() methods cannot be called on an element that has child elements. Line 10, position 8.
  Source=ThinkGeo.MapSuite.Wpf
  StackTrace:
   at ThinkGeo.MapSuite.Wpf.Tile.DrawException(GeoCanvas geoCanvas, Exception exception)
   at ThinkGeo.MapSuite.Wpf.Tile.Draw(GeoCanvas geoCanvas)
   at ThinkGeo.MapSuite.Wpf.Tile.Q1Y=(Object status)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

All the best,
Jonathan

Hi Jonathan,

It looks the node in your XML is omse:TopographicArea, but you write it in your code is osme:TopographicArea.

Do you thinks it’s the problem?

Regards,

Ethan

Ethan,

It looks like it was part of the problem. I no longer get an exception and the URL still looks to return data, but nothing actually displaying in the map window. Unfortunately I did the project quickly by adapting the wpf WMTS example (as I needed that as well) but I’m not really familiar with wpf - more used to winforms. So not 100% certain all setup correctly. The project did show WMTS tiles fine and I’ve just switched the WMTS layer to a WFS layer and added some default styling.

Regards,
Jonathan

Hi Jonathan,

You can try my sample code to see whether the Collection features have valid value.

If it contains the value, then you can see whether you forget to set style to it.

If your data is area, you should want to set the style like this:

        wfsFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Country1;
        wfsFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

Wish that’s helpful.

Regards,

Ethan

Hi Ethan,

Thanks for the suggestion. I had already set up some styles so didn’t immediately seem to make a difference, however after trying some lines and points layers I spotted that at some, quite zoomed out, zoom levels a few features were being displayed but only really visible because lines / points have a pixel width. I changed my default area style to have an outline of 3px and some things showed up. Not well as only at a very zoomed out level and vanish as you try to zoom in on them.

This probably relates to API only returning a maximum of 100 features per query, but odd about the zoom levels.

Jonathan

Hi Jonathan,

Because your server need a key, so I cannot watch the situation you mentioned.

It looks your question maybe the return feature have some problem so they hadn’t been rendered correct. And does the 100 features per query is the limitaion from server side?

I think you can check that like this:

  1. Double check the map unit is correct, and check the projection is correct.

  2. Zoom in to a deep level, and set the current extent to map, it can make sure the return features is easy to watch on map.

  3. Set style for both point, line and area, set a bigger size for border.

  4. Open the return XML out of map (Or get the WellKnownText from GetAllFeatures function), make sure the feature is contained in current extent.

And I think you can find where is the problem after check these items.

I think the possibility reason is the return features is not included in current extent, or only part of features are return, or the render logic have some problem so return feature hadn’t render correct on map.

If you think it’s still not easy to solve, and the server is private, you can create a ticket then upload the sample there, ticket system is private.

Regards,

Ethan

Ethan,

Thanks, I will try out the suggestions, although not today/tomorrow unfortunately as I have some other work commitments. Hopefully your ideas will lead to some positive progress and, either way, I will let you know how I get on.

Regards,
Jonathan

Hi Jonathan,

Any question or update please feel free to let us know.

Regards,

Ethan

Hi Ethan,

Here is the promised update on how we are getting on. I handed this over to a colleague called Sam for a while and he got quite a bit further. The first fix was swapping bounding box coordinates around like:

string bbox = string.Format("&BBOX={0},{1}", lowerLeft, lowerRight);    

e.WebRequest = WebRequest.Create("https://osdatahubapi.os.uk/omse/wfs?SERVICE=WFS&VERSION=1.0.0&REQUEST=GetFeature&TYPENAME=omse:TopographicArea" + bbox + "&propertyname=SHAPE&key=XXXXXXXX");

There are also issues with the way data has to be retrieved using the count and offset parameters. Essentially the API returns a maximum of 100 features, but you can repeatedly request by using the offset parameter until got them all. Is this something supported by the ThinkGeo WFS layer type? If not what methods would we have to override?

Regards,
Jonathan

Hi Jonathan,

Our WFS layer don’t have related functions.

Your solution is split one request to many requests to get more than 100 result.

So you should want to write a custom function to split current extent, and build request string to get result, then parse the return XML and convert them into Mapsuite features.

You can put the logic into “override GetFeaturesInsideBoundingBoxCore” and it should works.

Wish that’s helpful.

Regards,

Ethan

Hi Ethan,

Thanks for the suggestion. We will give it a go, however thinking about this I’m not certain it is the best approach? Mainly because:

  1. Hard to guarantee a part-bbox would have less that 100 features unless very tiny?
  2. Many features would overlap these part-bboxes, so issues either putting them together and/or rejecting duplicates

But, starting from the same place, we could somehow pass a call number to the create request and loop until get fewer than 100 returned. Very roughly like [not proper code]

Override GetFeaturesInBoundingBoxCore( ... )
{
    wfsLayer.CallCounter = 0;
    do
    {
        returnedFeatures = base. GetFeaturesInBoundingBoxCore()
        wfsLayer.CallCounter++;    
    } until (returnedFeatures.Count < 100)
}

Then in 
Layer_SendingWebRequest( ... )
{
    String block = string.format(“&count=100&offset={0}”,100* wfsLayer.CallCounter);

    //  Then use that in the request    
}

Regards,
Jonathan

Hi Jonathan,

Thanks for your update.

And I think your two questions is hard to get solved, just like you mentioned, the server side is black box for us, it don’t have an interface to make sure whether the 100 items is all items in target splitted area of current scale.

And if you change the offset very small and compare the result, it will get so many repeating features, it will make the performance very bad.

For your 2nd question, you can view the return value to see whether there is something like feature ID or feature name, so you can compare it and remove duplication ones. But it still relied to server side.

We don’t have better suggestion about the two problems.

Regards,

Ethan

Ethan,
OK, no problem and it’s with us to investigate now. Thanks for the help,
Jonathan

Hi Jonathan,

Thanks for your update.

Any question please feel free to let us know.

Regards,

Ethan