Project: s41467-025-61087-4 | Function: trajectory_displacement_error (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Calculates the trajectory displacement error (ADE/FDE) for evaluating tropical cyclone track predictions, converting normalized coordinates to actual geographical distances.

**KEY TERMS:** {
  "pred_traj": "predicted trajectory",
  "pred_traj_gt": "ground truth trajectory",
  "consider_ped": "batch-wise computation flag"
}

**CONTEXT CODE:** 
def gan_d_loss(scores_real, scores_fake):
    """
    Input:
    - scores_real: Tensor of shape (N,) giving scores for real samples
    - scores_fake: Tensor of shape (N,) giving scores for fake samples

    Output:
    - loss: Tensor of shape (,) giving GAN discriminator loss
    """
    y_real = torch.ones_like(scores_real) * random.uniform(0.7, 1.2)
    y_fake = torch.zeros_like(scores_fake) * random.uniform(0, 0.3)
    loss_real = bce_loss(scores_real, y_real)
    loss_fake = bce_loss(scores_fake, y_fake)
    return loss_real + loss_fake


def l2_loss(pred_traj, pred_traj_gt, loss_mask, random=0, mode='average'):
    """
    Input:
    - pred_traj: Tensor of shape (seq_len, batch, 2). Predicted trajectory.
    - pred_traj_gt: Tensor of shape (seq_len, batch, 2). Groud truth
    predictions.
    - loss_mask: Tensor of shape (batch, seq_len)
    - mode: Can be one of sum, average, raw
    Output:
    - loss: l2 loss depending on mode
    """
    seq_len, batch, _ = pred_traj.size()
    loss = (loss_mask.unsqueeze(dim=2) *
            (pred_traj_gt.permute(1, 0, 2) - pred_traj.permute(1, 0, 2))**2)
    if mode == 'sum':
        return torch.sum(loss)
    elif mode == 'average':
        return torch.sum(loss) / torch.numel(loss_mask.data)
    elif mode == 'raw':
        return loss.sum(dim=2).sum(dim=1)

def toNE(pred_traj,pred_Me):
    # 0  经度  1纬度
    if len(pred_traj.shape) == 2:
        pred_traj = pred_traj.unsqueeze(1)
        pred_Me = pred_Me.unsqueeze(1)
    pred_traj[:, :,0] = pred_traj[:, :,0] / 10 * 500 + 1800
    pred_traj[:,:,1] = pred_traj[:,:,1] / 6 * 300
    # 0 气压 1 风速
    pred_Me[:, :, 0] = pred_Me[:, :, 0]*50+960
    pred_Me[:, :, 1] = pred_Me[:, :, 1] * 25 + 40
    return pred_traj,pred_Me

def trajectory_displacement_error(pred_traj, pred_traj_gt, consider_ped=None, mode='sum'):
    import os
    if os.getenv('TEST_GOLDEN', 'true') == 'false':
        raise NotImplementedError('This function is masked for testing purposes.')
    """
    Input:
    - pred_traj: Tensor of shape (seq_len, batch, 2). Predicted trajectory.
    - pred_traj_gt: Tensor of shape (seq_len, batch, 2). Ground truth
    predictions.
    - consider_ped: Tensor of shape (batch)
    - mode: Can be one of sum, raw
    Output:
    - loss: gives the eculidian displacement error
    """
    seq_len, _, _ = pred_traj.size()
    loss = pred_traj_gt.permute(1, 0, 2) - pred_traj.permute(1, 0, 2)
    loss[:,:,0] = (loss[:,:,0]/10)*111*(pred_traj_gt.permute(1, 0, 2)[:,:,1]/10*np.pi/180).cos()
    # w0 = pred_traj_gt.permute(1, 0, 2)[:,:,1]/10
    # w = (pred_traj_gt.permute(1, 0, 2)[:,:,1]/10*np.pi/180)
    # w2 = (pred_traj_gt.permute(1, 0, 2)[:, :, 1] / 10 * np.pi / 180).cos()
    loss[:, :, 1] = (loss[:, :, 1] / 10) * 111
    loss = loss**2
    loss = torch.sqrt(loss[:,:,0]+loss[:,:,1])
    # if consider_ped is not None:
    #     loss = torch.sqrt(loss.sum(dim=2)).sum(dim=1) * consider_ped
    # else:
    #     loss = torch.sqrt(loss.sum(dim=2)).sum(dim=1)
    if mode == 'sum':
        return torch.sum(loss)
    elif mode == 'raw':
        return loss

def trajectory_diff(pred_traj, pred_traj_gt, consider_ped=None, mode='sum'):
    """
    Input:
    - pred_traj: Tensor of shape (seq_len, batch, 2). Predicted trajectory.
    - pred_traj_gt: Tensor of shape (seq_len, batch, 2). Ground truth
    predictions.
    - consider_ped: Tensor of shape (batch)
    - mode: Can be one of sum, raw
    Output:
    - loss: gives the eculidian displacement error
    """
    # print(pred_traj.size())
    seq_len, _, _ = pred_traj.size()
    loss = pred_traj.permute(1, 0, 2) - pred_traj_gt.permute(1, 0, 2)
    loss[:,:,0] = (loss[:,:,0]/10)*111
    # w0 = pred_traj_gt.permute(1, 0, 2)[:,:,1]/10
    # w = (pred_traj_gt.permute(1, 0, 2)[:,:,1]/10*np.pi/180)
    # w2 = (pred_traj_gt.permute(1, 0, 2)[:, :, 1] / 10 * np.pi / 180).cos()
    loss[:, :, 1] = (loss[:, :, 1] / 10) * 111*(pred_traj_gt.permute(1, 0, 2)[:,:,1]/10*np.pi/180).cos()
    # loss = loss**2
    # loss = torch.sqrt(loss[:,:,0]+loss[:,:,1])
    # if consider_ped is not None:
    #     loss = torch.sqrt(loss.sum(dim=2)).sum(dim=1) * consider_ped
    # else:
    #     loss = torch.sqrt(loss.sum(dim=2)).sum(dim=1)
    if mode == 'sum':
        return torch.sum(loss)
    elif mode == 'raw':
        return loss

def value_diff(pred_traj, pred_traj_gt, consider_ped=None, mode='sum'):
    """
    Input:
    - pred_traj: Tensor of shape (seq_len, batch, 2). Predicted trajectory.
    - pred_traj_gt: Tensor of shape (seq_len, batch, 2). Ground truth
    predictions.
    - consider_ped: Tensor of shape (batch)
    - mode: Can be one of sum, raw
    Output:
    - loss: gives the eculidian displacement error
    """
    seq_len, _, _ = pred_traj.size()
    loss = pred_traj.permute(1, 0, 2)-pred_traj_gt.permute(1, 0, 2)

    # loss = loss**2
    # loss = torch.sqrt(loss[:,:,0]+loss[:,:,1])
    # if consider_ped is not None:
    #     loss = torch.sqrt(loss.sum(dim=2)).sum(dim=1) * consider_ped
    # else:
    #     loss = torch.sqrt(loss.sum(dim=2)).sum(dim=1)
    if mode == 'sum':
        return torch.sum(loss)
    elif mode == 'raw':
        return loss

def value_error(pred_traj, pred_traj_gt, consider_ped=None, mode='sum'):
    """
    Input:
    - pred_traj: Tensor of shape (seq_len, batch, 2). Predicted trajectory.
    - pred_traj_gt: Tensor of shape (seq_len, batch, 2). Ground truth
    predictions.
    - consider_ped: Tensor of shape (batch)
    - mode: Can be one of sum, raw
    Output:
    - loss: gives the eculidian displacement error
    """
    seq_len, _, _ = pred_traj.size()
    loss = torch.abs((pred_traj.permute(1, 0, 2)-pred_traj_gt.permute(1, 0, 2)))


Complete the function implementation replacing "raise NotImplementedError".