コード例 #1
0
ファイル: CreateKmz.cs プロジェクト: pxg414/sharpkml
        public static void Run()
        {
            const string OutputPath = "output.kmz";
            string       input      = Program.GetInputFile("Enter a file to convert to Kmz:", "Data/doc.kml");

            try
            {
                KmlFile kml = LoadKml(input);

                using (KmzFile kmz = SaveKmlAndLinkedContentIntoAKmzArchive(kml, input))
                    using (Stream output = File.Create(OutputPath))
                    {
                        kmz.Save(output);
                        Console.WriteLine("Saved to '{0}'.", OutputPath);
                    }

                // Now open the file we saved and list the contents
                using (Stream file = File.OpenRead(OutputPath))
                    using (KmzFile kmz = KmzFile.Open(file))
                    {
                        Console.WriteLine("Contents:");
                        foreach (string name in kmz.Files)
                        {
                            Console.WriteLine(name);
                        }
                    }
            }
            catch (Exception ex)
            {
                Program.DisplayError(ex.GetType() + "\n" + ex.Message);
            }
        }
コード例 #2
0
        /// <summary>
        /// Basic output of the instances Kml to a Kmz as specified in configuration.
        /// </summary>
        public void Save(string band = "")
        {
            string FileName;

            if (String.IsNullOrWhiteSpace(settings.FileName))
            {
                if (band == "")
                {
                    FileName = $"{DateTime.Now.ToString("yyyy-MM-dd")} - Subscriber Map.kmz";
                }
                else
                {
                    FileName = $"{DateTime.Now.ToString("yyyy-MM-dd")} - Subscriber Map ({band}).kmz";
                }
            }
            else
            {
                FileName = settings.FileName;
            }

            KmlFile kmlFile = KmlFile.Create(OutputKml, true);

            using (FileStream fs = new FileStream(FileName, FileMode.Create))
            {
                using (KmzFile kmz = KmzFile.Create(kmlFile))
                {
                    kmz.Save(fs);
                }
            }
        }
コード例 #3
0
        public void SaveKmz(string outputPathFile)
        {
            var kmlFile = KmlFile.Create(Kml, true);

            using KmzFile kmz   = SaveKmlAndLinkedContentIntoAKmzArchive(kmlFile, outputPathFile);
            using Stream output = File.Create(outputPathFile);
            kmz.Save(output);
            Console.WriteLine("Saved to '{0}'.", outputPathFile);
        }
コード例 #4
0
        public void StartProcess(ProgressDialog dialog, DoWorkEventArgs e)
        {
            int   count = dialog.Maximum = binds.Count;
            int   done  = 0;
            Color c     = Color.FromArgb(255, 82, 82);

            dialog.ReportProgress(-1, null, "Экспорт данных..", null);

            var style = new Style();

            style.Id             = $"colorIcon_{c.R}_{c.G}_{c.B}";
            style.Icon           = new IconStyle();
            style.Icon.Color     = new Color32(255, c.B, c.G, c.R);
            style.Icon.ColorMode = ColorMode.Normal;
            style.Icon.Icon      = new IconStyle.IconLink(new Uri("http://www.gstatic.com/mapspro/images/stock/503-wht-blank_maps.png"));
            style.Icon.Scale     = 1;

            style.Label       = new LabelStyle();
            style.Label.Scale = 0;

            var document = new Document();

            document.Name = System.IO.Path.GetFileNameWithoutExtension(this.path);

            foreach (var bind in this.binds)
            {
                var placemark = CreatePlacemark(bind, style);
                document.AddFeature(placemark);

                done++;
                dialog.ReportProgress(done, null, $"Выполнено: {done}/{count}");
            }

            document.AddStyle(style);

            KmlFile kml = KmlFile.Create(document, false);
            KmzFile k   = KmzFile.Create(kml);

            if (System.IO.File.Exists(this.path))
            {
                System.IO.File.Delete(this.path);
            }

            using (var stream = System.IO.File.OpenWrite(this.path))
            {
                k.Save(stream);
            }
            e.Result = this.path;
        }
コード例 #5
0
        public static void SaveFile(KmlFile kmlFile, String filePath)
        {
            string fileExtension = System.IO.Path.GetExtension(filePath);

            using (FileStream fileStream = File.OpenWrite(filePath))
            {
                if (fileExtension.Equals(".kmz", StringComparison.OrdinalIgnoreCase))
                {
                    KmzFile kmzFile = KmzFile.Create(kmlFile);
                    kmzFile.Save(fileStream);
                }
                else
                {
                    kmlFile.Save(fileStream);
                }
            }
        }
コード例 #6
0
        // function that writes out KML file based on the flight chosen by the user
        private void CreateKMLButton_Click(object sender, EventArgs e)
        {
            int    nCount;
            int    nFlightID;
            string sfilename;
            long   lprevTimestamp;

            bLoggingEnabled = false;

            if (FlightPickerLV.SelectedItems.Count < 1)
            {
                MessageBox.Show("Please choose a flight before exporting.", "Export KML File");
                return;
            }

            if (KMLFilePathTBRO.Text.Length == 0)
            {
                MessageBox.Show("Please choose a folder location before exporting.", "Export KML File");
                return;
            }

            nFlightID = (int)FlightPickerLV.SelectedItems[0].Tag;

            // This is the root element of the file
            var    kml        = new Kml();
            Folder mainFolder = new Folder();

            mainFolder.Name        = String.Format("{0} {1}", FlightPickerLV.SelectedItems[0].SubItems[1].Text, FlightPickerLV.SelectedItems[0].SubItems[0].Text);
            mainFolder.Description = new Description
            {
                Text = "Overall Data for the flight"
            };

            kml.Feature = mainFolder;

            // start of Flight Path Line
            var placemarkLine = new Placemark();

            mainFolder.AddFeature(placemarkLine);
            placemarkLine.Name        = "Flight Path Line";
            placemarkLine.Description = new Description
            {
                Text = "Line of the flight"
            };
            var linestring           = new LineString();
            var coordinatecollection = new CoordinateCollection();

            linestring.Coordinates  = coordinatecollection;
            linestring.AltitudeMode = AltitudeMode.Absolute;

            SharpKml.Dom.LineStyle lineStyle = new SharpKml.Dom.LineStyle();
            lineStyle.Color = Color32.Parse("ff0000ff");
            lineStyle.Width = 5;
            Style flightStyle = new Style();

            flightStyle.Id     = "FlightStyle";
            flightStyle.Line   = lineStyle;
            linestring.Extrude = false;
            mainFolder.AddStyle(flightStyle);

            SharpKml.Dom.Style waypointStyle = new SharpKml.Dom.Style();
            waypointStyle.Id        = "WaypointStyle";
            waypointStyle.Icon      = new SharpKml.Dom.IconStyle();
            waypointStyle.Icon.Icon = new SharpKml.Dom.IconStyle.IconLink(new System.Uri("https://maps.google.com/mapfiles/kml/paddle/grn-square.png"));
            mainFolder.AddStyle(waypointStyle);

            SharpKml.Dom.Style pushpinblueStyle = new SharpKml.Dom.Style();
            pushpinblueStyle.Id        = "PushPinBlueStyle";
            pushpinblueStyle.Icon      = new SharpKml.Dom.IconStyle();
            pushpinblueStyle.Icon.Icon = new SharpKml.Dom.IconStyle.IconLink(new System.Uri("https://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png"));
            mainFolder.AddStyle(pushpinblueStyle);

            SharpKml.Dom.Style pushpingreenStyle = new SharpKml.Dom.Style();
            pushpingreenStyle.Id        = "PushPinGreenStyle";
            pushpingreenStyle.Icon      = new SharpKml.Dom.IconStyle();
            pushpingreenStyle.Icon.Icon = new SharpKml.Dom.IconStyle.IconLink(new System.Uri("https://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png"));
            mainFolder.AddStyle(pushpingreenStyle);

            placemarkLine.StyleUrl = new Uri("#FlightStyle", UriKind.Relative);

            List <FlightPathData> FlightPath = new List <FlightPathData>();

            FlightPath = FlightPathDB.GetFlightPathData(nFlightID);
            foreach (FlightPathData fpd in FlightPath)
            {
                coordinatecollection.Add(new Vector(fpd.latitude, fpd.longitude, fpd.altitude * 0.3048));
            }
            placemarkLine.Geometry = linestring;

            // start of Flight Plan Waypoints
            string sFromTo = "";
            List <FlightWaypointData> FlightWaypoints = new List <FlightWaypointData>();

            FlightWaypoints = FlightPathDB.GetFlightWaypoints(nFlightID);
            if (FlightWaypoints.Count > 0)
            {
                Folder FlightPlanFolder = new Folder();

                FlightPlanFolder.Name        = "Flight Plan";
                FlightPlanFolder.Description = new Description
                {
                    Text = "Waypoints along the flight plan"
                };
                mainFolder.AddFeature(FlightPlanFolder);
                foreach (FlightWaypointData waypointData in FlightWaypoints)
                {
                    var placemarkPoint = new Placemark();

                    placemarkPoint.Name     = waypointData.gps_wp_name;
                    placemarkPoint.StyleUrl = new System.Uri("#WaypointStyle", UriKind.Relative);
                    placemarkPoint.Geometry = new SharpKml.Dom.Point
                    {
                        Coordinate   = new Vector(waypointData.gps_wp_latitude, waypointData.gps_wp_longitude, (double)waypointData.gps_wp_altitude * 0.3048),
                        AltitudeMode = AltitudeMode.Absolute
                    };
                    placemarkPoint.Description = new Description
                    {
                        Text = String.Concat(String.Format("Coordinates ({0:0.0000}, {1:0.0000}, {2} feet)", waypointData.gps_wp_longitude, waypointData.gps_wp_latitude, waypointData.gps_wp_altitude))
                    };
                    FlightPlanFolder.AddFeature(placemarkPoint);
                }
                sFromTo = " " + FlightWaypoints.First().gps_wp_name + "-" + FlightWaypoints.Last().gps_wp_name;
            }

            // start of Flight Data Points
            Folder DataPointsfolder = new Folder();

            DataPointsfolder.Name        = "Flight Path Data Points";
            DataPointsfolder.Visibility  = false;
            DataPointsfolder.Description = new Description
            {
                Text = "Data Points along the flight path"
            };
            mainFolder.AddFeature(DataPointsfolder);

            nCount = 0;
            foreach (FlightPathData fpd in FlightPath)
            {
                var    placemarkPoint = new Placemark();
                string descriptioncard;
                bool   bAnyLightsOn = false;

                nCount++;
                // if Google Earth App then you need to turn off visibility on each data point also
                if (GoogleEarthAppRB.Checked == true)
                {
                    placemarkPoint.Visibility = false;
                }
                placemarkPoint.Name = String.Concat("Flight Data Point ", nCount.ToString());
                placemarkPoint.Id   = nCount.ToString();
                descriptioncard     = String.Concat("<br>Timestamp = ", new DateTime(fpd.timestamp).ToString());

                descriptioncard += String.Concat(String.Format("<br><br>Coordinates ({0:0.0000}, {1:0.0000}, {2} feet)", fpd.latitude, fpd.longitude, fpd.altitude));
                descriptioncard += String.Format("<br>Temperature: {0:0.00}C / {1:0.00}F", fpd.ambient_temperature, fpd.ambient_temperature * 9 / 5 + 32);
                descriptioncard += String.Format("<br>Wind: {0:0.00} knts from {1:0.00} degrees", fpd.ambient_wind_velocity, fpd.ambient_wind_direction);
                descriptioncard += String.Format("<br>Altitude Above Ground: {0} feet", fpd.altitudeaboveground);
                if (fpd.sim_on_ground == 1)
                {
                    descriptioncard += String.Format("<br>Plane Is On The Ground");
                }

                descriptioncard += String.Format("<br><br>Heading Indicator: {0:0.00} degrees", fpd.heading_indicator);
                descriptioncard += String.Format("<br>True Heading: {0:0.00} degrees", fpd.plane_heading_true);
                descriptioncard += String.Format("<br>Magnetic Heading {0:0.00} degrees", fpd.plane_heading_magnetic);

                descriptioncard += string.Format("<br><br>Airspeed Indicated: {0:0.00 knts}", fpd.plane_airspeed_indicated);
                descriptioncard += string.Format("<br>Airspeed True: {0:0.00} knts", fpd.airspeed_true);
                descriptioncard += string.Format("<br>Ground Velocity: {0:0.00} knts", fpd.ground_velocity);
                descriptioncard += string.Format("<br>Engine 1: {0} RPM", fpd.Eng1Rpm);
                if (fpd.Eng2Rpm > 0)
                {
                    descriptioncard += string.Format("<br>Engine 2: {0} RPM", fpd.Eng2Rpm);
                }
                if (fpd.Eng3Rpm > 0)
                {
                    descriptioncard += string.Format("<br>Engine 3: {0} RPM", fpd.Eng3Rpm);
                }
                if (fpd.Eng4Rpm > 0)
                {
                    descriptioncard += string.Format("<br>Engine 4: {0} RPM", fpd.Eng4Rpm);
                }

                descriptioncard += string.Format("<br><br>Pitch: {0:0.00} degrees {1}", Math.Abs(fpd.plane_pitch), fpd.plane_pitch < 0 ? "Up" : "Down");
                descriptioncard += string.Format("<br>Bank: {0:0.00} degrees {1}", Math.Abs(fpd.plane_bank), fpd.plane_bank < 0 ? "Right" : "Left");
                descriptioncard += string.Format("<br>Vertical Speed: {0:0} feet per minute", fpd.vertical_speed);
                descriptioncard += string.Concat("<br>Flaps Position: ", fpd.flaps_handle_position);

                descriptioncard += string.Concat("<br><br>Lights On: ");

                // go thru mask and set lights that are on
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Nav) == (int)FlightPathData.LightStates.Nav)
                {
                    descriptioncard += string.Concat("Nav, ");
                    bAnyLightsOn     = true;
                }
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Beacon) == (int)FlightPathData.LightStates.Beacon)
                {
                    descriptioncard += string.Concat("Beacon, ");
                    bAnyLightsOn     = true;
                }
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Landing) == (int)FlightPathData.LightStates.Landing)
                {
                    descriptioncard += string.Concat("Landing, ");
                    bAnyLightsOn     = true;
                }
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Taxi) == (int)FlightPathData.LightStates.Taxi)
                {
                    descriptioncard += string.Concat("Taxi, ");
                    bAnyLightsOn     = true;
                }
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Strobe) == (int)FlightPathData.LightStates.Strobe)
                {
                    descriptioncard += string.Concat("Strobe, ");
                    bAnyLightsOn     = true;
                }
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Panel) == (int)FlightPathData.LightStates.Panel)
                {
                    descriptioncard += string.Concat("Panel, ");
                    bAnyLightsOn     = true;
                }
                // commented out the following lights because most planes don't use them and it messes up the GA aircraft

                /*                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Recognition) == (int)FlightPathData.LightStates.Recognition)
                 *              {
                 *                  descriptioncard += string.Concat("Recognition, ");
                 *                  bAnyLightsOn = true;
                 *              }
                 *              if ((fpd.LightsMask & (int)FlightPathData.LightStates.Wing) == (int)FlightPathData.LightStates.Wing)
                 *              {
                 *                  descriptioncard += string.Concat("Wing, ");
                 *                  bAnyLightsOn = true;
                 *              }
                 *              if ((fpd.LightsMask & (int)FlightPathData.LightStates.Logo) == (int)FlightPathData.LightStates.Logo)
                 *              {
                 *                  descriptioncard += string.Concat("Logo, ");
                 *                  bAnyLightsOn = true;
                 *              }*/
                if ((fpd.LightsMask & (int)FlightPathData.LightStates.Cabin) == (int)FlightPathData.LightStates.Cabin)
                {
                    descriptioncard += string.Concat("Cabin, ");
                    bAnyLightsOn     = true;
                }

                // if there are no masks then put none else remove last two characters which are the comma and the space from above
                if (bAnyLightsOn == false)
                {
                    descriptioncard += string.Concat("None");
                }
                else
                {
                    descriptioncard = descriptioncard.Remove(descriptioncard.Length - 2, 2);
                }

                if (fpd.is_gear_retractable == 1)
                {
                    descriptioncard += String.Concat("<br>Gear Position: ", fpd.gear_handle_position == 1 ? "Down" : "Up");
                }
                if (fpd.spoiler_available == 1)
                {
                    descriptioncard += String.Concat("<br>Spoiler Position: ", fpd.spoilers_handle_position == 1 ? "On" : "Off");
                }
                if (fpd.stall_warning == 1)
                {
                    descriptioncard += "<br>Stall Warning";
                }
                if (fpd.overspeed_warning == 1)
                {
                    descriptioncard += "<br>Overspeed Warning";
                }

                placemarkPoint.Description = new Description
                {
                    Text = descriptioncard
                };

                // turned off showing time with data points as it caused issues of not showing in Google Earth
                // if user turned them off and then back on

                /*                placemarkPoint.Time = new SharpKml.Dom.Timestamp
                 *              {
                 *                  When = new DateTime(fpd.timestamp)
                 *              };*/
                if (fpd.sim_on_ground == 1)
                {
                    placemarkPoint.StyleUrl = new System.Uri("#PushPinGreenStyle", UriKind.Relative);
                }
                else
                {
                    placemarkPoint.StyleUrl = new System.Uri("#PushPinBlueStyle", UriKind.Relative);
                }

                placemarkPoint.Geometry = new SharpKml.Dom.Point
                {
                    Coordinate   = new Vector(fpd.latitude, fpd.longitude, (double)fpd.altitude * 0.3048),
                    AltitudeMode = AltitudeMode.Absolute
                };
                DataPointsfolder.AddFeature(placemarkPoint);
            }

            // add 1st person feature
            SharpKml.Dom.GX.Tour tour = new SharpKml.Dom.GX.Tour();
            tour.Name = "First Person View of Flight";
            kml.AddNamespacePrefix("gx", "http://www.google.com/kml/ext/2.2");
            SharpKml.Dom.GX.Playlist playlist = new SharpKml.Dom.GX.Playlist();
            tour.Playlist = playlist;
            mainFolder.AddFeature(tour);
            lprevTimestamp = 0;
            nCount         = 0;
            foreach (FlightPathData fpd in FlightPath)
            {
                nCount++;
                SharpKml.Dom.GX.FlyTo flyto = new SharpKml.Dom.GX.FlyTo();

                // assume duration will be based on difference between timestamps
                // if first time thru loop and don't have old time or user wants to speed up video playback above threshold
                // then set duration to 1 if it is greater than 1 else leave as-is
                flyto.Duration = (new DateTime(fpd.timestamp) - new DateTime(lprevTimestamp)).TotalMilliseconds / 1000;
                if ((lprevTimestamp == 0) ||
                    (SpeedUpVideoPlaybackCB.Checked == true))
                {
                    if (flyto.Duration > 1)
                    {
                        flyto.Duration = 1;
                    }
                }

                lprevTimestamp = fpd.timestamp;
                flyto.Mode     = SharpKml.Dom.GX.FlyToMode.Smooth;
                SharpKml.Dom.Camera cam = new SharpKml.Dom.Camera();
                cam.AltitudeMode = SharpKml.Dom.AltitudeMode.Absolute;
                cam.Latitude     = fpd.latitude;
                cam.Longitude    = fpd.longitude;
                cam.Altitude     = fpd.altitude * 0.3048;
                cam.Heading      = fpd.plane_heading_true;
                if (GoogleEarthAppRB.Checked == true)
                {
                    cam.Roll = fpd.plane_bank;
                }
                else
                {
                    cam.Roll = fpd.plane_bank * -1;
                }
                cam.Tilt = (90 - fpd.plane_pitch);

                SharpKml.Dom.Timestamp tstamp = new SharpKml.Dom.Timestamp();
                tstamp.When = new DateTime(fpd.timestamp);

                cam.GXTimePrimitive = tstamp;

                flyto.View = cam;
                playlist.AddTourPrimitive(flyto);

                // change it so balloons show during first person view (for potential future use)

                /*
                 * var placemarkPoint = new Placemark();
                 * placemarkPoint.TargetId = nCount.ToString();
                 * placemarkPoint.GXBalloonVisibility = true;
                 * SharpKml.Dom.Update update = new SharpKml.Dom.Update();
                 * update.AddUpdate(new ChangeCollection() { placemarkPoint });
                 * SharpKml.Dom.GX.AnimatedUpdate animatedUpdate = new SharpKml.Dom.GX.AnimatedUpdate();
                 * animatedUpdate.Update = update;
                 * playlist.AddTourPrimitive(animatedUpdate);*/
            }

            // File name
            string         sFileNameFormat = ExportAsKmzCB.Checked ? "kmz" : "kml";
            FlightListData flightData      = FlightPathDB.GetFlight(nFlightID);

            if (flightData != null)
            {
                sfilename = string.Format("{0:yyyy-MM-dd_HHmmss}{1} {2}.{3}", new DateTime(flightData.start_flight_timestamp), sFromTo, flightData.aircraft, sFileNameFormat);
            }
            else
            {
                sfilename = String.Format("{0}{1} {2}.{3}", FlightPickerLV.SelectedItems[0].SubItems[0].Text, sFromTo, FlightPickerLV.SelectedItems[0].SubItems[1].Text, sFileNameFormat);
            }
            char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
            var    validfilename        = new string(sfilename.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray());

            sfilename  = string.Concat(KMLFilePathTBRO.Text, "\\");
            sfilename += validfilename;

            System.IO.File.Delete(sfilename);

            // write out KML file
            KmlFile kmlfile = KmlFile.Create(kml, true);

            if (ExportAsKmzCB.Checked)
            {
                // Export as KMZ
                using (KmzFile kmzFile = KmzFile.Create(kmlfile))
                    using (var stream = System.IO.File.OpenWrite(sfilename))
                    {
                        kmzFile.Save(stream);
                    }
            }
            else
            {
                // Export as KML
                using (var stream = System.IO.File.OpenWrite(sfilename))
                {
                    kmlfile.Save(stream);
                }
            }

            string         exportMessageText = String.Format("Flight successfully exported to {0}", sfilename);
            MessageBoxIcon exportMessageIcon = MessageBoxIcon.Information;

            // Upload to FTP server
            if (UploadToFtpCB.Checked)
            {
                try
                {
                    Cursor = Cursors.WaitCursor;
                    FTPSettings ftpSettings = new FTPSettings();
                    if (ftpSettings.UseSFTP)
                    {
                        SFTPClient.UploadFile(sfilename);
                    }
                    else
                    {
                        FTPClient.UploadFile(sfilename);
                    }
                    exportMessageText += "\nand uploaded to FTP server.";
                }
                catch (Exception ex)
                {
                    exportMessageText += "\n\nAn error occured while uploading the file: " + ex.Message;
                    exportMessageIcon  = MessageBoxIcon.Warning;
                }
                finally
                {
                    Cursor = Cursors.Default;
                }
            }

            MessageBox.Show(exportMessageText, "Export KML File", MessageBoxButtons.OK, exportMessageIcon);
        }
コード例 #7
0
        public string Write(WriteParameters parameters)
        {
            KmlWriteParameters p = parameters as KmlWriteParameters;
            var boards           = p.Boards;

            if (boards.Count == 0)
            {
                return(null);
            }

            var colors = boards.Select(a => p.ColorsProvider.GetBoardColor(a)).Distinct().ToList();
            var styles = CreateStyles(colors).ToList();

            var document = new Document();

            document.Name = System.IO.Path.GetFileNameWithoutExtension(p.FilePath);

            foreach (var group in boards.GroupBy(a => a.Address.City))
            {
                Folder f = new Folder();
                f.Name = group.Key;
                foreach (var b in group)
                {
                    var    placemark = new Placemark();
                    var    c         = p.ColorsProvider.GetBoardColor(b);
                    string name      = string.IsNullOrEmpty(b.Code) ? (b.Address.ToShortString()) : (b.Code + " " + b.Address.ToShortString());
                    placemark.Name = name;
                    var styleId = styles.First(a => a.Id == $"colorIcon_{c.R}_{c.G}_{c.B}").Id;
                    placemark.StyleUrl = new Uri($"#{styleId}", UriKind.Relative);
                    placemark.Geometry = new SharpKml.Dom.Point
                    {
                        Coordinate = new Vector(b.Location.Latitude, b.Location.Longitude)
                    };

                    string description = $"{b.Type} {b.Size}, сторона {b.Side}";
                    if (b.Metrics != null && b.Metrics.OTS != 0)
                    {
                        description += $"<br>OTS: {b.Metrics.OTS} 000, GRP: {b.Metrics.GRP}";
                    }
                    description          += $"<img src=\"{b.Photo.ToString()}\" height =\"300px\" width=\"auto\"/>";
                    description           = $"<![CDATA[{description}]]>";
                    placemark.Description = new Description {
                        Text = description
                    };

                    f.AddFeature(placemark);
                }
                document.AddFeature(f);
            }

            foreach (var s in styles)
            {
                document.AddStyle(s);
            }

            KmlFile kml = KmlFile.Create(document, false);
            KmzFile k   = KmzFile.Create(kml);

            string path = p.FilePath;

            if (System.IO.File.Exists(path))
            {
                System.IO.File.Delete(path);
            }
            using (var stream = System.IO.File.OpenWrite(path))
            {
                k.Save(stream);
            }

            return(path);
        }