AliFlux / VectorTileRenderer

A comprehensive Vector Map Tile Renderer for .Net/C#
MIT License
187 stars 52 forks source link

Why does the renderer have to use BitmapSource ? #24

Open ststeiger opened 3 years ago

ststeiger commented 3 years ago

Your render method, aka. Task<BitmapSource> Render(... adds a dependency on Windows. That's an aweful shame. Now I get it, you want to use it in WPF.

But you could just as well use Skia everywhere in the renderer, and instead return a byte-array. Then you can create an extension method in WPF, that takes the byte-array and converts it into a BitmapSource. I've done that over here, and the result should run on Linux, too, provided Skia native dependencies are installed there. Plus I've added a Live-Raster-Tile-Server, created from the WPF-independent VectorTileRenderer. But I guess your whole point is that using your way, you don't have to do that conversion. Maybe you should instead take a skia-bitmap/canvas that writes to image-source as input parameter in an overload, then put the rest into an extension method in the WPF project. On the other hand, if you do that, you get a dependency on Skia in client-code, so you can't change the renderring method anymore. Maybe better just return a byte-array. That adds a dependency to nothing. Or create a WPF extension dll, where you wrap the skia calll in and return the BitmapSource. That way you can add the skia canvas overload as internal, and make internals visisble to the wpf-extension dll only.

One more thing: It should take an input-language as parameter. For example, in MapBox-GL, I load the respective labels like here (tested with russian characters) Coalesce("name:ru", "name:latin", "name:nonlatin") where "ru" would be parametrized as user-provided primary-language.

       gl._glMap.on('load', function ()
        {
            var keys = Object.keys(gl._glMap.style._layers);

            for (var i = 0; i < keys.length; ++i)
            {
                if (((gl._glMap.style._layers[keys[i]].layout || {})._values || {})["text-field"])
                {
                    if (gl._glMap.style._layers[keys[i]].layout._values["text-field"].value.value.indexOf("name:") != -1)
                    {
                        // var languageField = "name:" + getUserLanguage();
                        // gl._glMap.setLayoutProperty(keys[i], 'text-field', ["coalesce", ["get", languageField], ["get", "name:latin"], ["get", "name:nonlatin"]]);
                        gl._glMap.setLayoutProperty(keys[i], 'text-field', ["coalesce", ["get", "name:ru"], ["get", "name:latin"], ["get", "name:nonlatin"]]);
                    }

                }

            } // Next i

        });

Also, you can get rid of the dynamic in Style.cs, that way you don't need to add the whole system-dynamic runtime in NetStandard:

dynamic jObject = JObject.Parse(json);
foreach (JProperty jSource in jObject.sources)
foreach (var jLayer in jObject.layers)

becomes

JProperty srcs = jObject.Property("sources");
if (srcs != null && srcs.Type == JTokenType.Property && srcs.Value.Type == JTokenType.Object)
{
    foreach (JProperty jSource in ((JObject)srcs.Value).Properties()) {

and

JProperty layers = jObject.Property("layers");

if (layers != null && layers.Value != null && layers.Value.Type == JTokenType.Array)
{
    foreach (JObject jLayer in layers.Value)

Otherwise, I'd say it's pretty good work for 4 days of effort !