diff --git a/moje-statystyki.php b/moje-statystyki.php
index 9b03c08..81f53d0 100644
--- a/moje-statystyki.php
+++ b/moje-statystyki.php
@@ -431,14 +431,16 @@ function mystat_yearly_summary_page() {
SELECT
MONTH(date) as month_num,
SUM(distance) as total_distance,
- SUM(calories) as total_calories,
- SEC_TO_TIME(SUM(TIME_TO_SEC(duration))) as total_duration
+ SUM(calories) as total_calories,
+ SUM(TIME_TO_SEC(duration)) as total_seconds,
+ COUNT(id) as activity_count
FROM $table_activities
WHERE YEAR(date) = %d
GROUP BY month_num
ORDER BY month_num ASC
", $current_year);
+
$monthly_summary = $wpdb->get_results( $sql, OBJECT_K ); // OBJECT_K zwróci tablicę z kluczami będącymi numerami miesięcy
// Przygotowanie danych dla wszystkich 12 miesięcy
@@ -468,16 +470,14 @@ function mystat_yearly_summary_page() {
$full_year_summary[$i] = (object) [
'month_name' => $month_name,
'total_distance' => $data ? $data->total_distance : 0,
- 'total_calories' => $data ? $data->total_calories : 0,
- 'total_duration' => $data ? $data->total_duration : '00:00:00',
+ 'total_calories' => $data ? (int)$data->total_calories : 0,
+ 'total_seconds' => $data ? (int)$data->total_seconds : 0,
+ 'activity_count' => $data ? (int)$data->activity_count : 0,
];
$total_year_distance += $full_year_summary[$i]->total_distance;
+ $total_year_seconds += $full_year_summary[$i]->total_seconds;
$total_year_calories += $full_year_summary[$i]->total_calories;
- if ( $data && ! empty( $data->total_duration ) ) {
- list($h, $m, $s) = explode(':', $data->total_duration);
- $total_year_seconds += $h * 3600 + $m * 60 + $s;
- }
}
$total_year_hours = floor($total_year_seconds / 3600);
@@ -485,44 +485,105 @@ function mystat_yearly_summary_page() {
$total_year_duration_formatted = sprintf('%d godz. %d min.', $total_year_hours, $total_year_minutes);
// Przygotowanie danych dla wykresu
- $chart_labels = [];
- $chart_data_distance = [];
+ $chart_labels_js = [];
+ $chart_datasets = [
+ 'distance' => [],
+ 'duration' => [],
+ 'calories' => [],
+ 'activities' => [],
+ ];
+
foreach ($full_year_summary as $month_data) {
- $chart_labels[] = $month_data->month_name;
- $chart_data_distance[] = round((float)$month_data->total_distance, 2);
+ $chart_labels_js[] = $month_data->month_name;
+ $chart_datasets['distance'][] = round((float)$month_data->total_distance, 2);
+ $chart_datasets['duration'][] = round($month_data->total_seconds / 3600, 2); // w godzinach
+ $chart_datasets['calories'][] = $month_data->total_calories;
+ $chart_datasets['activities'][] = $month_data->activity_count;
}
// Włączenie skryptów dla Chart.js
wp_enqueue_script('chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', [], null, true);
wp_register_script('mystat-chart-loader', false);
wp_enqueue_script('mystat-chart-loader');
+
+ $chart_configs = [
+ 'distance' => [
+ 'label' => 'Dystans (km)',
+ 'data' => $chart_datasets['distance'],
+ 'backgroundColor' => 'rgba(52, 152, 219, 0.5)',
+ 'borderColor' => 'rgba(52, 152, 219, 1)',
+ 'yAxisLabel' => 'Kilometry'
+ ],
+ 'duration' => [
+ 'label' => 'Czas trwania (godz.)',
+ 'data' => $chart_datasets['duration'],
+ 'backgroundColor' => 'rgba(26, 188, 156, 0.5)',
+ 'borderColor' => 'rgba(26, 188, 156, 1)',
+ 'yAxisLabel' => 'Godziny'
+ ],
+ 'calories' => [
+ 'label' => 'Kalorie (kcal)',
+ 'data' => $chart_datasets['calories'],
+ 'backgroundColor' => 'rgba(231, 76, 60, 0.5)',
+ 'borderColor' => 'rgba(231, 76, 60, 1)',
+ 'yAxisLabel' => 'kcal'
+ ],
+ 'activities' => [
+ 'label' => 'Liczba aktywności',
+ 'data' => $chart_datasets['activities'],
+ 'backgroundColor' => 'rgba(241, 196, 15, 0.5)',
+ 'borderColor' => 'rgba(241, 196, 15, 1)',
+ 'yAxisLabel' => 'Ilość'
+ ],
+ ];
+
wp_add_inline_script('mystat-chart-loader', '
document.addEventListener("DOMContentLoaded", function() {
- const ctx = document.getElementById("mystatYearlyChart");
- if (!ctx) return;
- new Chart(ctx, {
- type: "bar",
- data: {
- labels: ' . json_encode($chart_labels) . ',
- datasets: [{
- label: "Dystans (km)",
- data: ' . json_encode($chart_data_distance) . ',
- backgroundColor: "rgba(52, 152, 219, 0.5)",
- borderColor: "rgba(52, 152, 219, 1)",
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- scales: {
- y: {
- beginAtZero: true,
- title: { display: true, text: "Kilometry" }
- }
- }
- }
+ const chartLabels = ' . json_encode($chart_labels_js) . ';
+ const chartConfigs = ' . json_encode($chart_configs) . ';
+ let activeChart = null;
+
+ const tabs = document.querySelectorAll(".nav-tab");
+ tabs.forEach(tab => {
+ tab.addEventListener("click", function(e) {
+ e.preventDefault();
+ tabs.forEach(t => t.classList.remove("nav-tab-active"));
+ this.classList.add("nav-tab-active");
+
+ const chartType = this.getAttribute("href").substring(1);
+ renderChart(chartType);
+ });
});
+
+ function renderChart(type) {
+ if (activeChart) {
+ activeChart.destroy();
+ }
+ const config = chartConfigs[type];
+ const ctx = document.getElementById("mystatYearlyChart").getContext("2d");
+ activeChart = new Chart(ctx, {
+ type: "bar",
+ data: {
+ labels: chartLabels,
+ datasets: [{
+ label: config.label,
+ data: config.data,
+ backgroundColor: config.backgroundColor,
+ borderColor: config.borderColor,
+ borderWidth: 1
+ }]
+ },
+ options: {
+ responsive: true, maintainAspectRatio: false,
+ scales: { y: { beginAtZero: true, title: { display: true, text: config.yAxisLabel } } }
+ }
+ });
+ }
+
+ // Render initial chart
+ if (tabs.length > 0) {
+ renderChart(tabs[0].getAttribute("href").substring(1));
+ }
});
');
@@ -546,8 +607,14 @@ function mystat_yearly_summary_page() {
-
+
+
@@ -564,7 +631,7 @@ function mystat_yearly_summary_page() {
month_name ); ?> |
total_distance, 2, ',', ' ' ); ?> |
total_calories, 0, ',', ' ' ); ?> |
-
total_duration ); ?> |
+
total_seconds) ); ?> |
@@ -580,12 +647,12 @@ function mystat_yearly_summary_page() {
}
/**
- * Fetches and parses a GPX file from a URL to extract track points.
+ * Fetches and parses a GPX file from a URL to extract track points and elevation profile.
*
* @param string $gpx_url The URL of the GPX file.
- * @return array An array of [lat, lon] coordinates, or an empty array on failure.
+ * @return array An array containing 'points' for the map and 'elevation_profile'.
*/
-function mystat_get_track_from_gpx_url( $gpx_url ) {
+function mystat_parse_gpx_data( $gpx_url ) {
if ( empty( $gpx_url ) ) {
return [];
}
@@ -609,18 +676,68 @@ function mystat_get_track_from_gpx_url( $gpx_url ) {
return [];
}
- $points = [];
+ $track_data = [
+ 'points' => [],
+ 'elevation_profile' => [],
+ ];
+ $raw_points = [];
+
+ // Extract raw points with lat, lon, ele
if ( isset( $gpx->trk ) ) {
foreach ( $gpx->trk->trkseg as $trkseg ) {
foreach ( $trkseg->trkpt as $trkpt ) {
- if ( isset( $trkpt['lat'] ) && isset( $trkpt['lon'] ) ) {
- $points[] = [ (float) $trkpt['lat'], (float) $trkpt['lon'] ];
+ if ( isset( $trkpt['lat'], $trkpt['lon'] ) ) {
+ $raw_points[] = [
+ 'lat' => (float) $trkpt['lat'],
+ 'lon' => (float) $trkpt['lon'],
+ 'ele' => isset( $trkpt->ele ) ? (float) $trkpt->ele : null,
+ ];
}
}
}
}
- return $points;
+ if ( empty( $raw_points ) ) {
+ return $track_data;
+ }
+
+ // Process raw points to calculate profile
+ $cumulative_distance = 0;
+ $elevation_profile = [];
+ $map_points = [];
+
+ $haversine = function( $lat1, $lon1, $lat2, $lon2 ) {
+ $earth_radius = 6371; // in km
+ $dLat = deg2rad( $lat2 - $lat1 );
+ $dLon = deg2rad( $lon2 - $lon1 );
+ $a = sin( $dLat / 2 ) * sin( $dLat / 2 ) +
+ cos( deg2rad( $lat1 ) ) * cos( deg2rad( $lat2 ) ) *
+ sin( $dLon / 2 ) * sin( $dLon / 2 );
+ $c = 2 * atan2( sqrt( $a ), sqrt( 1 - $a ) );
+ return $earth_radius * $c;
+ };
+
+ foreach ( $raw_points as $i => $point ) {
+ $map_points[] = [ $point['lat'], $point['lon'] ];
+
+ if ( $i > 0 ) {
+ $prev_point = $raw_points[ $i - 1 ];
+ $distance_increment = $haversine( $prev_point['lat'], $prev_point['lon'], $point['lat'], $point['lon'] );
+ $cumulative_distance += $distance_increment;
+ }
+
+ if ( ! is_null( $point['ele'] ) ) {
+ $elevation_profile[] = [
+ 'distance' => round( $cumulative_distance, 3 ), // in km
+ 'elevation' => round( $point['ele'], 2 ), // in m
+ ];
+ }
+ }
+
+ $track_data['points'] = $map_points;
+ $track_data['elevation_profile'] = $elevation_profile;
+
+ return $track_data;
}
function mystat_infographic_page() {
@@ -1123,35 +1240,68 @@ function mystat_view_activity_page() {
}
};
- // Prepare map data if GPX exists
- $track_points = [];
+ // Prepare map and chart data if GPX exists
+ $gpx_data = [];
if ( ! empty( $activity->gpx_url ) ) {
- // Note: For performance, you might want to cache the parsed points in post meta
- // rather than parsing the file on every page load.
- $track_points = mystat_get_track_from_gpx_url( $activity->gpx_url );
+ $gpx_data = mystat_parse_gpx_data( $activity->gpx_url );
- if ( ! empty( $track_points ) ) {
- // Enqueue Leaflet assets. For simplicity, this is done here.
- // A better approach is to use the 'admin_enqueue_scripts' hook.
+ if ( ! empty( $gpx_data['points'] ) ) {
wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css');
wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', [], '1.9.4', true);
+ wp_enqueue_script('chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', [], null, true);
- // Pass track data to a script
- wp_register_script('mystat-map-loader', false);
- wp_enqueue_script('mystat-map-loader');
- wp_add_inline_script('mystat-map-loader',
- 'const mystat_track_points = ' . json_encode($track_points) . ';' .
- 'document.addEventListener("DOMContentLoaded", function() {
- if (typeof L === "undefined" || typeof mystat_track_points === "undefined" || mystat_track_points.length === 0) return;
+ wp_register_script('mystat-details-loader', false);
+ wp_enqueue_script('mystat-details-loader');
+
+ $map_script = '
+ const track_points = ' . json_encode($gpx_data['points']) . ';
+ if (typeof L !== "undefined" && track_points.length > 0) {
const map = L.map("mystat-activity-map");
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: \'© OpenStreetMap contributors\'
}).addTo(map);
- const polyline = L.polyline(mystat_track_points, {color: "#' . esc_js( ltrim($activity->color, '#') ) . '"}).addTo(map);
+ const polyline = L.polyline(track_points, {color: "' . esc_js( $activity->color ) . '"}).addTo(map);
map.fitBounds(polyline.getBounds().pad(0.1));
- L.marker(mystat_track_points[0]).addTo(map).bindPopup("Start");
- L.marker(mystat_track_points[mystat_track_points.length - 1]).addTo(map).bindPopup("Koniec");
- });'
+ L.marker(track_points[0]).addTo(map).bindPopup("Start");
+ L.marker(track_points[track_points.length - 1]).addTo(map).bindPopup("Koniec");
+ }
+ ';
+
+ $elevation_chart_script = '';
+ if ( ! empty( $gpx_data['elevation_profile'] ) ) {
+ $elevation_chart_script = '
+ const elevation_data = ' . json_encode( $gpx_data['elevation_profile'] ) . ';
+ if (typeof Chart !== "undefined" && elevation_data.length > 0) {
+ const ctx = document.getElementById("mystat-elevation-chart").getContext("2d");
+ new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: elevation_data.map(p => p.distance),
+ datasets: [{
+ label: "Wysokość (m)",
+ data: elevation_data.map(p => p.elevation),
+ borderColor: "' . esc_js( $activity->color ) . '",
+ backgroundColor: "' . esc_js( $activity->color ) . '20",
+ fill: true,
+ pointRadius: 0,
+ tension: 0.1
+ }]
+ },
+ options: {
+ responsive: true, maintainAspectRatio: false,
+ scales: {
+ x: { title: { display: true, text: "Dystans (km)" } },
+ y: { title: { display: true, text: "Wysokość (m n.p.m.)" } }
+ },
+ plugins: { legend: { display: false } }
+ }
+ });
+ }
+ ';
+ }
+
+ wp_add_inline_script('mystat-details-loader',
+ 'document.addEventListener("DOMContentLoaded", function() {' . $map_script . $elevation_chart_script . '});'
);
}
}
@@ -1211,10 +1361,18 @@ function mystat_view_activity_page() {
-
+
Mapa Trasy
-
+
+
+
+ Profil Wysokości
+
+
+
+
+
gpx_url ) ) : ?>
Mapa Trasy
@@ -1790,7 +1948,7 @@ function mystat_single_activity_shortcode_handler( $atts ) {
// Pobieranie danych z bazy
$table_activities = $wpdb->prefix . 'mystat_activities';
$sql = $wpdb->prepare("
- SELECT a.*, c.name as category_name, et.name as event_type_name, eq.name as equipment_name
+ SELECT a.*, c.name as category_name, c.color as category_color, et.name as event_type_name, eq.name as equipment_name
FROM $table_activities a
LEFT JOIN {$wpdb->prefix}mystat_categories c ON a.category_id = c.id
LEFT JOIN {$wpdb->prefix}mystat_event_types et ON a.event_type_id = et.id
@@ -1817,37 +1975,75 @@ function mystat_single_activity_shortcode_handler( $atts ) {
ob_start();
// Prepare map data if GPX exists
- $track_points = [];
+ $gpx_data = [];
if ( ! empty( $activity->gpx_url ) ) {
- $track_points = mystat_get_track_from_gpx_url( $activity->gpx_url );
+ $gpx_data = mystat_parse_gpx_data( $activity->gpx_url );
- if ( ! empty( $track_points ) ) {
+ if ( ! empty( $gpx_data['points'] ) ) {
// Enqueue scripts for the frontend
wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css');
wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', [], '1.9.4', true);
+ wp_enqueue_script('chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', [], null, true);
$map_id = 'mystat-map-' . esc_attr( $activity->id );
+ $chart_id = 'mystat-chart-' . esc_attr( $activity->id );
// Pass track data to a script
- wp_register_script('mystat-shortcode-map-loader-' . $activity->id, false);
- wp_enqueue_script('mystat-shortcode-map-loader-' . $activity->id);
- wp_add_inline_script('mystat-shortcode-map-loader-' . $activity->id,
+ wp_register_script('mystat-shortcode-loader-' . $activity->id, false);
+ wp_enqueue_script('mystat-shortcode-loader-' . $activity->id);
+
+ $map_script = '
+ const trackPoints = ' . json_encode( $gpx_data['points'] ) . ';
+ const mapId = "' . esc_js($map_id) . '";
+
+ var container = L.DomUtil.get(mapId);
+ if(container != null) { container._leaflet_id = null; }
+
+ const map = L.map(mapId);
+ L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
+ attribution: \'© OpenStreetMap\'
+ }).addTo(map);
+
+ const polyline = L.polyline(trackPoints, {color: "' . esc_js( $activity->category_color ) . '"}).addTo(map);
+ map.fitBounds(polyline.getBounds().pad(0.1));
+ ';
+
+ $elevation_chart_script = '';
+ if ( ! empty( $gpx_data['elevation_profile'] ) ) {
+ $elevation_chart_script = '
+ const elevationData = ' . json_encode( $gpx_data['elevation_profile'] ) . ';
+ const chartId = "' . esc_js($chart_id) . '";
+ const ctx = document.getElementById(chartId).getContext("2d");
+ new Chart(ctx, {
+ type: "line",
+ data: {
+ labels: elevationData.map(p => p.distance),
+ datasets: [{
+ label: "Wysokość (m)",
+ data: elevationData.map(p => p.elevation),
+ borderColor: "' . esc_js( $activity->category_color ) . '",
+ backgroundColor: "' . esc_js( $activity->category_color ) . '20",
+ fill: true, pointRadius: 0, tension: 0.1
+ }]
+ },
+ options: {
+ responsive: true, maintainAspectRatio: false,
+ scales: {
+ x: { title: { display: true, text: "Dystans (km)" } },
+ y: { title: { display: true, text: "Wysokość (m n.p.m.)" } }
+ },
+ plugins: { legend: { display: false } }
+ }
+ });
+ ';
+ }
+
+ wp_add_inline_script('mystat-shortcode-loader-' . $activity->id,
'(function() {
document.addEventListener("DOMContentLoaded", function() {
- if (typeof L === "undefined") return;
- const trackPoints = ' . json_encode( $track_points ) . ';
- const mapId = "' . esc_js($map_id) . '";
-
- var container = L.DomUtil.get(mapId);
- if(container != null) { container._leaflet_id = null; }
-
- const map = L.map(mapId);
- L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
- attribution: \'© OpenStreetMap\'
- }).addTo(map);
-
- const polyline = L.polyline(trackPoints, {color: "#3498db"}).addTo(map);
- map.fitBounds(polyline.getBounds().pad(0.1));
+ if (typeof L === "undefined" || typeof Chart === "undefined") return;
+ ' . $map_script . '
+ ' . $elevation_chart_script . '
});
})();'
);
@@ -1883,8 +2079,14 @@ function mystat_single_activity_shortcode_handler( $atts ) {
-
-
+
+
+
+
+
+
+
+