In my previous post I thought that I would not make the app because I did not had a nice geocoding service. I did find Azure Maps, but there is no nice C# client at that time and I did not feel like doing manual REST calls. I know I can, I just didn’t feel like making it (sorry).

So I’ve worked in the past with the Geocoding.Net Nuget packages and decided to go for the Bing version. Googles version requires an account with billing because the first 200 dollar is free. It does feel like a hurdle for me as I do not work that much with Google Services.

So I did create an Azure Function which reads the kmz and extracts it to kml and for each unknown placemark would do a geo-location and store it in a file, so that only new bears would require a call to the Geocoding service of Bing.

The app is up now in the play store. Not in Apples App store, because I did not buy a license for it.

https://play.google.com/store/apps/details?id=com.companyname.bosscheberenbingo

I forgot to rename the package.

Here is a small snippet for the Geocoding:

var geocoder = new Geocoding.Microsoft.BingMapsGeocoder(“key”);

var geoCodeResponse = await geocoder.GeocodeAsync(“address here”);

var location = geoCodeResponse.First().Coordinates;


Good luck and happy bear hunting!

Pin on pinterest Plus on Googleplus Post on LinkedIn

To keep kids entertained, people from all over the world put a teddy bear in their window so that kids can spot them during a walk.

I don’t know the origin. It’s in Canada, USA, New Zealand, UK etc. It’s also in my hometown so I decided to make an app for it.

People made a Facebook group with a google form to submit the teddy bear and an URL with all bears on google maps.

I thought that I had to get access to the Google spreadsheet containing the data, but it seems that it would not hold that much info if we take a look at the entry form and the maps data. In Google maps you can download a KMZ file which is a zipped KML (Keyhole Markup Language) It’s XML. Here is the full KML:

full-kml

So there is a web link in it to get the live data. I used the webclient to pull it in and read it and tried to make pins out of it.

My first step was to add the Xamarin.Forms.Map Nuget package and the SharpKml.Core

Here is the full code:


private void AddMap()
{
	var map = new Map(MapSpan.FromCenterAndRadius(new Position(51.697815, 5.303675), Distance.FromMeters(10000)));

	using (var client = new WebClient())
	{
		var kmz = KmzFile.Open(client.OpenRead("https://www.google.com/maps/d/u/0/kml?mid=1kedGv2twtsWmzgxRpZcu5hr-qpE77plL"));
		Kml kml = kmz.GetDefaultKmlFile().Root as Kml;

		if (kml != null)
		{
			foreach (Placemark placemark in kml.Flatten().OfType())
			{
				Console.WriteLine(placemark.Name);

				var pin = new Pin()
				{
					Address = placemark.Address,
					Label = placemark.Name,
					Type = PinType.Place
				};
				map.Pins.Add(pin);
			}
		}
	}

	this.Content = map;
}

But the pins won’t show up (of course). Because I did not set the position. The real Kml contains data like this:

kml-detail

So as you can see there is no Latitude Longitude for the placemarks… So how does google maps work then? It seems that both Google Maps and Google Earth Geocode the address to get the lat lng. But that service unfortunately is not free. I tried to load the kml in Google Earth and export it, but that also does not add the latitude and longitude. I also thought that kml support for the map control could be used. So that I could just provide the kml to the map and have it sort it out, but that was build with monoandroid 9 instead of netstandard2.0 and would probably not fix the geocode issue.

I planned to make an app with no central backend. But because of geocoding, I would have to use a webapi or Azure Function to keep track of the “database” with all teddy bears and their corresponding lat lng. That would also lower the requests for geocoding if it would be moved from the phone (client) to the server.

But I’ve not found a good free geocoder

nominatim.openstreetmap.org does not work if I would provide the kml data. I think that I will come back to this one….


Good luck making your own KML/KMZ reading app!

Pin on pinterest Plus on Googleplus Post on LinkedIn
0 Comments

It’s a bit of a follow up from my achievement last June when I passed the 70-486 MVC exam. I thought that I’d get the MCSD because I completed two (old/previous) UWP related exams before. But there was a bug in the cert planner. I had to wait a long time to hear from Microsoft, but unfortunately I had to take another exam to get the MCSD. I choose 70-532 because I wanted to go for MCSA: Cloud platform. So I ordered the v2 of the exam guide and studied every day during my holiday.

I found out (later) that the 70-532 and 70-535 and some more, will be deprecated by the end of the year… and will be replaced.

70-532 →   AZ-200
AZ-201
70-535 → AZ-300
AZ-301

But if you finish 532 or 535 before the 31ste of December you can use a cheaper transition exam to transfer to the same as the new “AZ” style. Since I passed 70-532, I decided to give AZ-202 a go and I hope that I will pass. I have to pass before June 2019 to get the MCAD. After that, I have added the AZ-300 and AZ-301 in my to-do list to get the MCASA (Microsoft Certified Azure Solutions Architect)

azure developer image

But first, let me enjoy my MCSD.

Good luck studying!

Pin on pinterest Plus on Googleplus Post on LinkedIn

I have blogged before about this Excel Nuget package where you don’t need to use interop and have Excel installed on the server. And my journey to start this Azure Function. This is because the most recent Excel format uses xml under the cover in a zipped file stored as a file with an xlsx extension. Since you do not have hard disk access in a serverless environment like Azure Functions you need to generate the Excel in memory (or store stuff in blob storage). I chose the in memory to leave no footprints or take up space in the cloud.

I wanted to use an Azure function to have it run in the cloud. Not being dependent on a Server which needs updates, reboots etc. Since the database already is in the Azure Cloud (Azure SQL) this seems a good/perfect fit.

I got the option to go for Azure function v1 or v2 which is in preview. So this was a nice opportunity to use the v2 and .Net Core/Standard. https://docs.microsoft.com/en-us/azure/azure-functions/functions-versions

The v2 also has support for the Office365 Graph. But that was out of (my) scope.

I took a timer based project because I wanted it to send an overview of invoices on a monthly basis. The Timer based project has a timer as data annotation based on CRON scheduling. There is however a small difference. Instead of 5 “fields” the Azure function has 6. It also let’s you schedule the seconds.

https://en.wikipedia.org/wiki/Cron#CRON_expression

So not just: minutes, hours, day of month, month, day of week, year but seconds, minutes, hours, day of month, month, day of week, year. Of course the order is really important. https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer#cron-expressions

I used this Nuget for the Excel export https://www.nuget.org/packages/EPPlus/

it has .Net Core support and will work perfectly.

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net;
using System.Net.Mail;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using OfficeOpenXml;

namespace MonthlyMailInvoices
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static void Run([TimerTrigger("10 0 0 1 * *")]TimerInfo myTimer, TraceWriter log)
        {
            log.Info($"C# Timer trigger function executed at: {DateTime.Now}");

            var com = new SqlCommand("SELECT * FROM [dbo].[INVOICES] where invoicedate > @startdt and invoicedate < @enddt");

            com.Parameters.AddWithValue("startdt", DateTime.Now.AddMonths(-1));
            com.Parameters.AddWithValue("enddt", DateTime.Now.AddDays(-1));

            var dt = new DataTable();

            using (var con = new SqlConnection("connectionstring goes here"))
            {
                con.Open();
                com.Connection = con;
                var da = new SqlDataAdapter(com);
                da.Fill(dt);

                log.Info($"start: {DateTime.Now.AddMonths(-1)} and end { DateTime.Now.AddDays(-1) } gave {dt.Rows.Count}");
            }

            using (var wb = new ExcelPackage())
            {
                wb.Workbook.Worksheets.Add("Our company");
                var ws = wb.Workbook.Worksheets[0];

                FillData(ws, dt, "Our company B.V.");

                var msg = new MailMessage();
                msg.To.Add("mymail@companydomain.com");
                msg.Subject = "Montly invoices";
                msg.From = new MailAddress("the@cloud.com");
                msg.Body = $"Invoices from {DateTime.Now.AddMonths(-1)} to { DateTime.Now.AddDays(-1) } in the Excel attachment.";
                var ms = new MemoryStream(wb.GetAsByteArray());
                ms.Position = 0;

                //msg.Attachments.Add(new Attachment(ms, "Invoices.xlsx", "application/vnd.ms-excel"));
                msg.Attachments.Add(new Attachment(ms, "Invoices.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
                var smtp = new SmtpClient
                {
                    Host = "smtp.gmail.com",
                    Port = 587,
                    EnableSsl = true,
                    Credentials = new NetworkCredential("my@gmailaccount.com", "incorrectpassword")
                };
                smtp.Send(msg);
            }
        }

        private static void FillData(ExcelWorksheet ws, DataTable dt, string title)
        {
            ws.Cells[1, 1].Value = title;

            ws.Cells[2, 1].Value = "Invoice nr";
            ws.Cells[2, 2].Value = "Invoice date";
            ws.Cells[2, 3].Value = "Amount inc. VAT";
            ws.Cells[2, 4].Value = "VAT";
            ws.Cells[2, 5].Value = "Amount exc. VAT";

            int row = 3;

            foreach (DataRow dr in dt.Rows)
            {
                ws.Cells[row, 1].Value = dr[0].ToString();
                ws.Cells[row, 2].Value = dr[1].ToString();
                ws.Cells[row, 3].Value = dr[2].ToString();
                ws.Cells[row, 4].Value = dr[3].ToString();
                ws.Cells[row++, 5].Value = dr[4].ToString();
            }
        }
    }
}

I could not test it locally because I had some issues with logins for my localdb. So I hit publish to deploy it on Azure. However republishing failed. I found the answer (as always) on StackOverflow. I had to add “MSDEPLOY_RENAME_LOCKED_FILES” and set it to 1 (true).

app-settings

Tony gave the correct solution.

I also had issues with the Excel generating in memory and having the Memorystream to a byte array and providing the right Mime type. Found that too on SO.

The last bit was to automate deployment. I had my code in VSTS (git) and configured a CI/CD pipeline (build + release) But had issues to grant myself (personal account) global admin rights from our company account in order to be able to access Azure resources to deploy. It was a matter of time before the Azure rights/roles changes are active. It’s a nice small serverless function which you can (should) add to source control and ci/cd to automate the latest builds to a test or production environment in the cloud.


Good luck!

Pin on pinterest Plus on Googleplus Post on LinkedIn
0 Comments

I have written powershell scripts in Azure runbooks in Azure Automation. It’s not a new concept. It’s even from back in 2014

https://azure.microsoft.com/en-us/blog/azure-automation-your-sql-agent-in-the-cloud/

I started to use it because there is no SQL Agent in Azure SQL databases. I relied on SQL Agent to perform Ola’s database maintenance scripts. I use the Azure automation with Runbooks now for a long time to build reports from Azure SQL and have them send to people by SMTP.

The problem is that I string concatenate HTML in the powershell script and just put the results in an HTML enabled Email message. It is still a good option… Until a coworker requests an Excel attached to the mail…

Excel in Azure Runbook (Powershell)

I did build the powershell locally first.

image

When using the Azure Automation ISE add-on for Windows PowerShell ISE it hit me. The cloud probably has no Excel com/interop…

So I found this module to work with Excel in Powershell without Excel on GitHub. It uses Epplus. Which I mentioned in my post from 6 years ago.

But I realised that I could also just use Azure Functions and code in C# and have a time trigger. This enables me to write my beloved C# rather then scripting Powershell. I can also just use the Epplus nuget package.

The Azure functions v2 are now in preview and have .Net Standard support (which is great!)

The Visual Studio dialog can be unclear if you visit it for the first time and have no clue that the schedule uses CRON notation. Maybe they will change it, but now you know.


Good luck!

Pin on pinterest Plus on Googleplus Post on LinkedIn