HarmonyOS NEXT Development Case: Latitude and Longitude Distance Calculation

The following example demonstrates how to implement a distance calculator between geographic coordinates using HarmonyOS NEXT's declarative development paradigm. This case leverages ArkUI components and the MapKit module to create a responsive interface with real-time distance calculation capabilities. Key Features Dual-column input for coordinates (longitude/latitude) Real-time distance calculation using MapKit APIs Example preset (Beijing to Shanghai) Responsive UI with focus states Data clearing functionality Kilometer-based distance display Code Implementation (with English Comments) import { mapCommon } from '@kit.MapKit'; // Import map common module import { map } from '@kit.MapKit'; // Import map module @Entry // Entry decorator for application entry component @Component // Component decorator struct DistanceCalculator { // Distance calculator component structure @State private primaryColor: string = '#fea024'; // Primary theme color @State private fontColor: string = "#2e2e2e"; // Main text color @State private isStartFocused: boolean = false; // Start point input focus state @State private isEndFocused: boolean = false; // End point input focus state @State private isSecondStartFocused: boolean = false; // Second start point focus @State private isSecondEndFocused: boolean = false; // Second end point focus @State private baseSpacing: number = 30; // Base spacing unit @State @Watch('onInputChange') private startLongitude: string = ""; // Start longitude @State @Watch('onInputChange') private startLatitude: string = ""; // Start latitude @State @Watch('onInputChange') private endLongitude: string = ""; // End longitude @State @Watch('onInputChange') private endLatitude: string = ""; // End latitude @State distance: number = 0; // Calculated distance aboutToAppear(): void { // Component lifecycle hook this.onInputChange(); // Initial calculation } onInputChange() { // Input change handler let fromLatLng: mapCommon.LatLng = { // Start coordinate object latitude: Number(this.startLatitude), longitude: Number(this.startLongitude) }; let toLatLng: mapCommon.LatLng = { // End coordinate object latitude: Number(this.endLatitude), longitude: Number(this.endLongitude) }; this.distance = map.calculateDistance(fromLatLng, toLatLng); // Calculate distance } build() { // UI construction Column() { // Main vertical layout // Header section Text("Coordinate Distance Calculator") .width('100%') .height(54) .fontSize(18) .fontWeight(FontWeight.Bold) .backgroundColor(Color.White) .textAlign(TextAlign.Center) .fontColor(this.fontColor); // Input area Column() { Row() { // Example presets row Text('Example (Beijing->Shanghai)') .fontColor("#5871ce") .fontSize(18) .padding(`${this.baseSpacing / 2}lpx`) .backgroundColor("#f2f1fd") .borderRadius(5) .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) .onClick(() => { // Preset coordinates this.startLongitude = "116.4074"; // Beijing longitude this.startLatitude = "39.9042"; // Beijing latitude this.endLongitude = "121.4737"; // Shanghai longitude this.endLatitude = "31.2304"; // Shanghai latitude }); Blank(); // Spacer Text('Clear All') .fontColor("#e48742") .fontSize(18) .padding(`${this.baseSpacing / 2}lpx`) .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 }) .backgroundColor("#ffefe6") .borderRadius(5) .onClick(() => { // Clear inputs this.startLongitude = ""; this.startLatitude = ""; this.endLongitude = ""; this.endLatitude = ""; }); }.height(45) .justifyContent(FlexAlign.SpaceBetween) .width('100%'); Divider().margin({ top: 5, bottom: 5 }); // Start point inputs Row() { Text('Start Point') .fontWeight(FontWeight.Bold) .fontSize(18) .fontColor(this.fontColor); }.margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` }); Row() { // Longitude/Latitude inputs TextInput({ text: $$this.startLongitude, placeholder: 'Longitude' }) .caretColor(this.primaryColor) .layoutWeight(1) .type(InputType.NUMBER_DECIMAL) .placeholderColor(this.isStartFocused ? this.primaryColor : Color.Gray) .fontColor(this.isStartFocused ? this.primaryColor : this.fontColor) .borderColor(this.isStartFocused ? this.primaryColor : Color.Gray) .borderWidth(1) .borderRadius(10) .backgroundColor(Color.White)

May 11, 2025 - 05:43
 0
HarmonyOS NEXT Development Case: Latitude and Longitude Distance Calculation

Image description

The following example demonstrates how to implement a distance calculator between geographic coordinates using HarmonyOS NEXT's declarative development paradigm. This case leverages ArkUI components and the MapKit module to create a responsive interface with real-time distance calculation capabilities.

Key Features

  • Dual-column input for coordinates (longitude/latitude)
  • Real-time distance calculation using MapKit APIs
  • Example preset (Beijing to Shanghai)
  • Responsive UI with focus states
  • Data clearing functionality
  • Kilometer-based distance display

Code Implementation (with English Comments)

import { mapCommon } from '@kit.MapKit'; // Import map common module
import { map } from '@kit.MapKit'; // Import map module

@Entry // Entry decorator for application entry component
@Component // Component decorator
struct DistanceCalculator { // Distance calculator component structure
  @State private primaryColor: string = '#fea024'; // Primary theme color
  @State private fontColor: string = "#2e2e2e"; // Main text color
  @State private isStartFocused: boolean = false; // Start point input focus state
  @State private isEndFocused: boolean = false; // End point input focus state
  @State private isSecondStartFocused: boolean = false; // Second start point focus
  @State private isSecondEndFocused: boolean = false; // Second end point focus
  @State private baseSpacing: number = 30; // Base spacing unit
  @State @Watch('onInputChange') private startLongitude: string = ""; // Start longitude
  @State @Watch('onInputChange') private startLatitude: string = ""; // Start latitude
  @State @Watch('onInputChange') private endLongitude: string = ""; // End longitude
  @State @Watch('onInputChange') private endLatitude: string = ""; // End latitude
  @State distance: number = 0; // Calculated distance

  aboutToAppear(): void { // Component lifecycle hook
    this.onInputChange(); // Initial calculation
  }

  onInputChange() { // Input change handler
    let fromLatLng: mapCommon.LatLng = { // Start coordinate object
      latitude: Number(this.startLatitude),
      longitude: Number(this.startLongitude)
    };
    let toLatLng: mapCommon.LatLng = { // End coordinate object
      latitude: Number(this.endLatitude),
      longitude: Number(this.endLongitude)
    };
    this.distance = map.calculateDistance(fromLatLng, toLatLng); // Calculate distance
  }

  build() { // UI construction
    Column() { // Main vertical layout
      // Header section
      Text("Coordinate Distance Calculator")
        .width('100%')
        .height(54)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .backgroundColor(Color.White)
        .textAlign(TextAlign.Center)
        .fontColor(this.fontColor);

      // Input area
      Column() {
        Row() { // Example presets row
          Text('Example (Beijing->Shanghai)')
            .fontColor("#5871ce")
            .fontSize(18)
            .padding(`${this.baseSpacing / 2}lpx`)
            .backgroundColor("#f2f1fd")
            .borderRadius(5)
            .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 })
            .onClick(() => { // Preset coordinates
              this.startLongitude = "116.4074"; // Beijing longitude
              this.startLatitude = "39.9042"; // Beijing latitude
              this.endLongitude = "121.4737"; // Shanghai longitude
              this.endLatitude = "31.2304"; // Shanghai latitude
            });

          Blank(); // Spacer

          Text('Clear All')
            .fontColor("#e48742")
            .fontSize(18)
            .padding(`${this.baseSpacing / 2}lpx`)
            .clickEffect({ level: ClickEffectLevel.LIGHT, scale: 0.8 })
            .backgroundColor("#ffefe6")
            .borderRadius(5)
            .onClick(() => { // Clear inputs
              this.startLongitude = "";
              this.startLatitude = "";
              this.endLongitude = "";
              this.endLatitude = "";
            });
        }.height(45)
        .justifyContent(FlexAlign.SpaceBetween)
        .width('100%');

        Divider().margin({ top: 5, bottom: 5 });

        // Start point inputs
        Row() {
          Text('Start Point')
            .fontWeight(FontWeight.Bold)
            .fontSize(18)
            .fontColor(this.fontColor);
        }.margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` });

        Row() { // Longitude/Latitude inputs
          TextInput({ text: $$this.startLongitude, placeholder: 'Longitude' })
            .caretColor(this.primaryColor)
            .layoutWeight(1)
            .type(InputType.NUMBER_DECIMAL)
            .placeholderColor(this.isStartFocused ? this.primaryColor : Color.Gray)
            .fontColor(this.isStartFocused ? this.primaryColor : this.fontColor)
            .borderColor(this.isStartFocused ? this.primaryColor : Color.Gray)
            .borderWidth(1)
            .borderRadius(10)
            .backgroundColor(Color.White)
            .showUnderline(false)
            .onBlur(() => this.isStartFocused = false)
            .onFocus(() => this.isStartFocused = true);

          Line().width(10);

          TextInput({ text: $$this.startLatitude, placeholder: 'Latitude' })
            .caretColor(this.primaryColor)
            .layoutWeight(1)
            .type(InputType.NUMBER_DECIMAL)
            .placeholderColor(this.isEndFocused ? this.primaryColor : Color.Gray)
            .fontColor(this.isEndFocused ? this.primaryColor : this.fontColor)
            .borderColor(this.isEndFocused ? this.primaryColor : Color.Gray)
            .borderWidth(1)
            .borderRadius(10)
            .backgroundColor(Color.White)
            .showUnderline(false)
            .onBlur(() => this.isEndFocused = false)
            .onFocus(() => this.isEndFocused = true);
        }

        // End point inputs
        Text('End Point')
          .fontWeight(FontWeight.Bold)
          .fontSize(18)
          .fontColor(this.fontColor)
          .margin({ bottom: `${this.baseSpacing}lpx`, top: `${this.baseSpacing}lpx` });

        Row() {
          TextInput({ text: $$this.endLongitude, placeholder: 'Longitude' })
            .caretColor(this.primaryColor)
            .layoutWeight(1)
            .type(InputType.NUMBER_DECIMAL)
            .placeholderColor(this.isSecondStartFocused ? this.primaryColor : Color.Gray)
            .fontColor(this.isSecondStartFocused ? this.primaryColor : this.fontColor)
            .borderColor(this.isSecondStartFocused ? this.primaryColor : Color.Gray)
            .borderWidth(1)
            .borderRadius(10)
            .backgroundColor(Color.White)
            .showUnderline(false)
            .onBlur(() => this.isSecondStartFocused = false)
            .onFocus(() => this.isSecondStartFocused = true);

          Line().width(10);

          TextInput({ text: $$this.endLatitude, placeholder: 'Latitude' })
            .caretColor(this.primaryColor)
            .layoutWeight(1)
            .type(InputType.NUMBER_DECIMAL)
            .placeholderColor(this.isSecondEndFocused ? this.primaryColor : Color.Gray)
            .fontColor(this.isSecondEndFocused ? this.primaryColor : this.fontColor)
            .borderColor(this.isSecondEndFocused ? this.primaryColor : Color.Gray)
            .borderWidth(1)
            .borderRadius(10)
            .backgroundColor(Color.White)
            .showUnderline(false)
            .onBlur(() => this.isSecondEndFocused = false)
            .onFocus(() => this.isSecondEndFocused = true);
        }
      }.width('650lpx')
      .padding(`${this.baseSpacing}lpx`)
      .margin({ top: 20 })
      .backgroundColor(Color.White)
      .borderRadius(10)
      .alignItems(HorizontalAlign.Start);

      // Result display
      Column() {
        Text() {
          Span(`Distance: `)
          Span(`${(this.distance / 1000).toFixed(2)} `).fontColor(this.primaryColor)
          Span(`kilometers`)
        }
        .fontWeight(FontWeight.Bold)
        .fontSize(18)
        .fontColor(this.fontColor);
      }.width('650lpx')
      .backgroundColor(Color.White)
      .borderRadius(10)
      .padding(`${this.baseSpacing}lpx`)
      .margin({ top: `${this.baseSpacing}lpx` })
      .alignItems(HorizontalAlign.Start);
    }
    .height('100%')
    .width('100%')
    .backgroundColor("#eff0f3");
  }
}

Implementation Details

  1. State Management:
  2. Uses @State decorators for reactive UI updates
  3. @Watch decorator triggers recalculation on input changes
  4. Manages focus states for visual feedback

  5. Map Integration:

  6. Utilizes map.calculateDistance() from MapKit

  7. Converts string inputs to numeric coordinates

  8. Returns distance in meters (converted to kilometers)

  9. UI Features:

  10. Responsive layout using percentage-based widths

  11. Visual feedback for input focus states

  12. Clean material design-inspired aesthetics

  13. Adaptive color schemes

  14. Smooth animations for user interactions

  15. Validation:

  16. Input type restricted to decimal numbers

  17. Automatic handling of invalid inputs (returns 0 distance)

  18. Empty state management

This implementation demonstrates HarmonyOS NEXT's capabilities in creating sophisticated location-based applications with clean, maintainable code. The declarative UI approach combined with reactive programming patterns enables efficient development of complex interactive applications.