20 ) ); if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) { return array(); } $gpx_content = wp_remote_retrieve_body( $response ); if ( empty( $gpx_content ) ) { return array(); } // --- Privacy Zone --- $privacy_options = get_option( 'statpress_privacy_options' ); $privacy_enabled = ! empty( $privacy_options['latitude'] ) && ! empty( $privacy_options['longitude'] ) && ! empty( $privacy_options['radius'] ); $privacy_center_lat = $privacy_enabled ? (float) $privacy_options['latitude'] : 0; $privacy_center_lon = $privacy_enabled ? (float) $privacy_options['longitude'] : 0; $privacy_radius_km = $privacy_enabled ? ( (int) $privacy_options['radius'] ) / 1000 : 0; // Convert meters to km libxml_use_internal_errors( true ); $gpx = simplexml_load_string( $gpx_content ); libxml_clear_errors(); if ( false === $gpx ) { return array(); } // Use XPath to be more robust against different GPX structures/namespaces $gpx->registerXPathNamespace( 'gpx', 'http://www.topografix.com/GPX/1/1' ); $trackpoints = $gpx->xpath( '//gpx:trkpt' ); if ( empty( $trackpoints ) ) { $trackpoints = $gpx->xpath( '//trkpt' ); // Fallback for files without namespace } $raw_points = array(); $start_time = null; foreach ( $trackpoints as $trkpt ) { if ( isset( $trkpt['lat'], $trkpt['lon'] ) ) { $extensions = $trkpt->extensions ? $trkpt->extensions->children( 'gpxtpx', true ) : null; $time = isset( $trkpt->time ) ? strtotime( (string) $trkpt->time ) : null; if ( $time && is_null( $start_time ) ) { $start_time = $time; } $hr_val = ( $extensions && isset( $extensions->TrackPointExtension->hr ) ) ? (int) $extensions->TrackPointExtension->hr : null; $cad_val = ( $extensions && isset( $extensions->TrackPointExtension->cad ) ) ? (int) $extensions->TrackPointExtension->cad : null; $raw_points[] = array( 'lat' => (float) $trkpt['lat'], 'lon' => (float) $trkpt['lon'], 'ele' => isset( $trkpt->ele ) ? (float) $trkpt->ele : null, 'time_offset' => $start_time && $time ? $time - $start_time : null, 'hr' => $hr_val, 'cad' => $cad_val, ); } } if ( empty( $raw_points ) ) { return array(); } // Process raw points to calculate profiles $map_points = array(); $profiles = array( 'distance' => array(), 'time' => array(), 'elevation' => array(), 'speed' => array(), 'hr' => array(), 'cadence' => array(), ); $cumulative_distance = 0; $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 ) { $is_in_privacy_zone = false; if ( $privacy_enabled ) { $distance_from_center = $haversine( $privacy_center_lat, $privacy_center_lon, $point['lat'], $point['lon'] ); if ( $distance_from_center <= $privacy_radius_km ) { $is_in_privacy_zone = true; } } if ( ! $is_in_privacy_zone ) { $map_points[] = array( $point['lat'], $point['lon'] ); $speed = null; if ( $i > 0 ) { $prev_point = $raw_points[ $i - 1 ]; $distance_delta = $haversine( $prev_point['lat'], $prev_point['lon'], $point['lat'], $point['lon'] ); // km $cumulative_distance += $distance_delta; if ( ! is_null( $point['time_offset'] ) && ! is_null( $prev_point['time_offset'] ) ) { $time_delta = $point['time_offset'] - $prev_point['time_offset']; // seconds if ( $time_delta > 0 ) { $speed = ( $distance_delta * 3600 ) / $time_delta; // km/h } } } $profiles['distance'][] = round( $cumulative_distance, 3 ); $profiles['time'][] = $point['time_offset']; $profiles['elevation'][] = ! is_null( $point['ele'] ) ? round( $point['ele'], 2 ) : null; $profiles['speed'][] = ! is_null( $speed ) ? round( $speed, 1 ) : null; $profiles['hr'][] = $point['hr']; $profiles['cadence'][] = $point['cad']; } } return array( 'points' => $map_points, 'profiles' => $profiles, ); }