/// <summary> /// Loads the given KMZ or KMX and store it in mTrackData. /// </summary> private void parseKmxOrKmz(string kmxPath) { mTrackData = null; try { XmlDocument doc = new XmlDocument(); if (ZipFile.IsZipFile(kmxPath)) { using (ZipFile zf = ZipFile.Read(kmxPath)) { foreach (ZipEntry ze in zf) { // We take the first name that ends with .kmx if (ze.FileName.EndsWith(".kml")) { mLabelTrackDataLoadResults.Text = "Parsing " + ze.FileName; Application.DoEvents(); MemoryStream ms = new MemoryStream(); ze.Extract(ms); ms.Seek(0, SeekOrigin.Begin); doc.Load(ms); mTrackData = new TrackParser(doc); } } } } else { // Read the kml file directly mLabelTrackDataLoadResults.Text = "Parsing " + Path.GetFileName(kmxPath); Application.DoEvents(); doc.Load(kmxPath); mTrackData = new TrackParser(doc); } mLabelTrackDataLoadResults.Text = mTrackData.Summary; } catch (FileNotFoundException) { mLabelTrackDataLoadResults.Text = "File not found"; } catch (Exception e) { mLabelTrackDataLoadResults.Text = "Error: " + e.Message; Log("Failed to load " + kmxPath + ": " + e.Message + "\n" + e.StackTrace); } mStatusBar.Text = null; }
/// <summary> /// Creates a new generator. /// Note that for a preview you just use destFilename=null. /// </summary> /// <param name="fps">The desired frames per second, e.g. 30</param> /// <param name="movieSx">Movie container width in pixels</param> /// <param name="movieSy">Movie container height in pixels</param> /// <param name="trackSx">Track rendering width in pixels</param> /// <param name="trackSy">Track rendering height in pixels</param> /// <param name="trackData">Track data</param> /// <param name="destFilename">AVI destination filename. Null for a preview.</param> public Generator(int fps, int movieSx, int movieSy, int trackSx, int trackSy, TrackParser trackData, string destFilename) { mFps = fps; mMovieSx = movieSx; mMovieSy = movieSy; mTrackSx = trackSx; mTrackSy = trackSy; mTrackData = trackData; mDestFilename = destFilename; }
public Interpolator(TrackParser trackData) { mTrackData = trackData; mCurrInterpol = new TrackParser.Sample(); }
private Bitmap prepareTrackBmp(Rectangle trackRect, Gps2PixelProjection trackProj, TrackParser td) { // We need at least a lap to draw something if (!td.HasSamples || trackProj == null) { return(null); } // We'll draw one of the laps. Just get the first lap. TrackParser.Lap currLap = td.Laps[0]; int w = trackRect.Width; int h = trackRect.Height; Bitmap bmp = new Bitmap(w, h); using (Graphics g = Graphics.FromImage(bmp)) { using (Brush bgColor = new SolidBrush(Color.Gray), trackColor = new SolidBrush(Color.Yellow)) { using (Pen trackPen = new Pen(trackColor, kTrackWidth)) { g.FillRectangle(bgColor, 0, 0, w, h); List <TrackParser.Sample> samples = td.Samples; TrackParser.Sample s = samples[0]; CPointF last = new CPointF(); CPointF next = new CPointF(); transformGpsCoord(s.mLongtiude, s.mLatitude, trackProj, last); float px = (float)trackProj.mPixelOffsetX; float py = (float)trackProj.mPixelOffsetY; last.mX -= px; last.mY -= py; for (int i = 1, n = samples.Count; i < n; i++) { s = samples[i]; if (s.mLap != currLap) { continue; } transformGpsCoord(s.mLongtiude, s.mLatitude, trackProj, next); next.mX -= px; next.mY -= py; g.DrawLine(trackPen, last.mX, last.mY, next.mX, next.mY); last.mX = next.mX; last.mY = next.mY; } } } } return(bmp); }
// --- GPS to track rect & bitmap --- private Gps2PixelProjection prepareTrackProj(Rectangle rect, TrackParser td, out Rectangle trackRect) { // we need at least one point to do something if (!td.HasSamples) { trackRect = Rectangle.Empty; return(null); } double minLong = Double.PositiveInfinity, maxLong = Double.NegativeInfinity, minLat = Double.PositiveInfinity, maxLat = Double.NegativeInfinity; foreach (TrackParser.Sample d in td.Samples) { minLong = Math.Min(minLong, d.mLongtiude); maxLong = Math.Max(maxLong, d.mLongtiude); minLat = Math.Min(minLat, d.mLatitude); maxLat = Math.Max(maxLat, d.mLatitude); } // we want to map the min..maxLong(+X)..Lat(-Y) to the given rect // rect top left corner (it's x/y base) corresponds to minLong/maxLat. float x = rect.X; float y = rect.Y; float w = rect.Width; float h = rect.Height; // currently use a square part of the dest rect if (w > h) { w = h; } else { h = w; } // adjust for track border x += kTrackBorder; y += kTrackBorder; w -= 2 * kTrackBorder; h -= 2 * kTrackBorder; trackRect = new Rectangle((int)x, (int)y, (int)w, (int)h); // Compute offset and scaling to transform a coord point into a screen point Gps2PixelProjection proj = new Gps2PixelProjection(); proj.mGpsOffsetX = minLong; proj.mGpsOffsetY = maxLat; proj.mPixelOffsetX = x; proj.mPixelOffsetY = y; proj.mGpsScaleX = (maxLong > minLong) ? w / (maxLong - minLong) : 0; proj.mGpsScaleY = (maxLat > minLat) ? h / (minLat - maxLat) : 0; return(proj); }
private void threadEntryPoint() { int fps = mFps; int msx = mMovieSx; int msy = mMovieSy; int tsx = mTrackSx; int tsy = mTrackSy; TrackParser td = mTrackData; using (Graphics g = Graphics.FromImage(mAviBmp)) { using (Brush chromaColor = new SolidBrush(Color.Blue), bgColor = new SolidBrush(Color.Gray), posColor = new SolidBrush(Color.Red), textColor = new SolidBrush(Color.Orange)) { Rectangle bgRect = new Rectangle(msx - tsx, msy - tsy, tsx, tsy); int nbFrames = (int)(mTrackData.TotalTime * fps); // prepare track drawing (map GPS coord => screen coord: offset + scale) Rectangle trackRect; Gps2PixelProjection trackProj = prepareTrackProj(bgRect, td, out trackRect); Bitmap trackBmp = prepareTrackBmp(trackRect, trackProj, td); // prepare g-meter pos // prepare text positions PointF[] textPos; using (Font labelFont = prepareText(bgRect, out textPos), numFont = new Font(FontFamily.GenericMonospace, labelFont.Size, FontStyle.Bold)) { Interpolator interp = new Interpolator(td); double invFps = 1 / (double)fps; bool userStopRequested = false; for (int frame = 0, updateFps = 0; frame < nbFrames && !userStopRequested; frame++, updateFps++) { double currTime = (double)frame * invFps; TrackParser.Sample currSample = interp.GoTo(currTime); double currLapTime = interp.CurrLapTime; // keep track of current dot & interpolate between dots // draw background g.FillRectangle(chromaColor, 0, 0, msx, msy); g.FillRectangle(bgColor, bgRect); // draw track + current pos drawTrackPos(g, posColor, trackRect, trackBmp, trackProj, currSample.mLongtiude, currSample.mLatitude); // draw bearing // TODO // draw text drawText(g, textColor, labelFont, numFont, textPos, currSample.mSpeed, currSample.mAccel, currSample.mLateralAccel, currTime, currLapTime, interp.CurrLapIndex, interp.LastLapDuration); // finally dump frame and update preview/progress MainModule.MainForm.Invoke(mAddFrameFunc); if (updateFps == fps) { updateFps = 0; } if (updateFps == 0) { // Update status, progress, etc. userStopRequested = syncUpdate(frame, nbFrames, new Bitmap(mAviBmp)); } if (mThreadMustStop) { break; } } } // using Font // Make sure to tell owner that the generator is done // This one must be async -- this thread will quit and the owner will // try to join to wait for the thread to finish. asyncUpdate(nbFrames, nbFrames, new Bitmap(mAviBmp)); } // using Brush } // using Graphics }