Skip to main content

MapEHR Introduction

MapEHR is an engine that implements mappings between FHIR (or any other data source) and openEHR.

The MapEHR engine processes the incoming data into openEHR compositions for storage in an openEHR repository.

MapEHR Mapping Specification

MapEHR is an open, vendor-neutral specification for mapping FHIR (or any other data) to openEHR to FHIR. You can write mappings in a human friendly YAML format. The vendor-neutral approach ensures that any engine capable of running these mappings can execute them.

The specification is published to https://www.schemastore.org/json/. Search for MapEHR or visit directly. Autocomplete is supported in most editors (IntelliJ IDE, Visual Studio Code,...).

The Basics

Mapping rules are stored in easy to read and write YAML files. Mapping engine receives a list of directories in which it searches for the rules. The directory structure is up to the users – engine searches all sub-folders of the received directories.

Each YAML file may contain one or more rule (under rules:).

Map a start time in composition

A simple rule to map effectiveDateTime found in FHIR data to context.start_time attribute in openEHR composition:

templates:
openEHR-EHR-COMPOSITION.simple-encounter-blood-pressure.v0:
name: Blood pressure
uri: https://ckm.openehr.org/ckm/archetypes/1013.1.3574
map:
- path: /context[id9002]
element:
attribute: start_time
formats:
fhir:
- attribute: effectiveDateTime

In the above example /context[id9002]/start_time will be set to the effectiveDateTime found in the FHIR data.


Map simple attributes in an archetype

Another simple rule to set a /subject/name attribute with a subject.reference from the FHIR data:

archetypes:
openEHR-EHR-OBSERVATION.blood_pressure.v2:
name: Blood pressure
uri: https://ckm.openehr.org/ckm/archetypes/1013.1.3574
formats:
fhir:
name: Observationbp
uri: https://build.fhir.org/bp.html
resource: Observation
map:
- path: /subject
element:
attribute: name
formats:
fhir:
- attribute: subject
path: reference

The rules below set /data[id2|History|]/origin and /data[id2|History|]/events[id7|Any event|]/time using the effectiveDateTime from the FHIR data:

- path: /data[id2|History|]
element:
attribute: origin
formats:
fhir:
- attribute: effectiveDateTime
- path: /data[id2|History|]/events[id7|Any event|]
element:
attribute: time
formats:
fhir:
- attribute: effectiveDateTime

Use a library method to set values

A value may get a value assigned by calling a library method. In the below example we use rm.CodePhrase('IANA', 'UTF-8') to set /content/encoding which is a CODE_PHRASE type in openEHR:

- path: /encoding
element:
value: rm.CodePhrase('IANA', 'UTF-8')

The above example will result in the following data in the composition:

  "encoding": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "IANA"
},
"code_string": "UTF-8"
},

Set element based on a terminology code

Mapping often uses terminology codes for matching the right properties in FHIR data:

- path: /protocol[id12|Tree|]/items
elements:
body_site:
value_type: DV_CODED_TEXT
formats:
fhir:
- attribute: bodySite
path: coding.code
elements:
body_site:
one_of:
- body_site_snomed
define:
body_site: id15|Location of measurement|
body_site_snomed: http://snomed.info/id/368209003

In the above example the id15|Location of measurement| will be mapped to bodySite with http://snomed.info/id/368209003 SNOMED CT code.


Map multiple values

We can map multiple values at the same time. In the below example systolic, diastolic and clinical_interpretation elements are set for a blood pressure:

- path: /data[id2|History|]/events[id7|Any event|]/data[id4|Data|]/items
elements:
systolic:
value_type: DV_QUANTITY
diastolic:
value_type: DV_QUANTITY
clinical_interpretation:
value_type: DV_CODED_TEXT
formats:
fhir:
- attribute: component
path: code.coding.code
elements:
systolic:
one_of:
- systolic_loinc
- systolic_snomed
diastolic:
one_of:
- diastolic_loinc
- attribute: interpretation
path: coding.system
elements:
clinical_interpretation:
one_of:
- clinical_interpretation_hl7
define:
systolic: id5|Systolic|
diastolic: id6|Diastolic|
clinical_interpretation: id1060|Clinical interpretation|
systolic_loinc: http://loinc.org/8480-6
diastolic_loinc: http://loinc.org/8462-4
systolic_snomed: http://snomed.info/id/271649006
diastolic_snomed: http://snomed.info/id/271650006
clinical_interpretation_hl7: http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation

In the above example the id5|Systolic| will be mapped to one of the FHIR components based on LOINC or SNOMED CT codes. Similarly the id6|Diastolic|.

The element for id1060|Clinical interpretation| will be matched based on the HL7 code system http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation.


Use other elements to calculate a value

Sometimes we need to use other elements to calculate a value of another element. Calculating Body Mass Index (BMI) is such an example.

We use LOINC code 85353-1 to find the nearest common ancestor of all the elements we need. In this case the LOINC code is specified for a Composition (see openEHR-EHR-COMPOSITION.simple-encounter-blood-pressure.v0).

We use attribute: content to select COMPOSITION.content which is a list of OBSERVATIONs. For the BMI we need weight and height.

We use weight_observation element (with LOINC code 29463-7) to first select the OBSERVATION for openEHR-EHR-OBSERVATION.t_vital_signs-weight.v1.0.0. We need to go one level deeper to find the actual value for weight. This is achieved with using attribute: data to search inside OBSERVATION.data list. We use weight element (with LOINC code 29463-7) to find the weight ELEMENT. Note that in this case the OBSERVATION and the ELEMENT we need, both use the same LOINC code. This is why we defined weight_observation and weight separately even if they use the same LOINC code. The difference is that the first one holds the OBSERVATION element and the second one the ELEMENT element. We will use the weight of type ELEMENT in the bmi element.

Similarly we use height_observation and height to read the value for the height.

The BMI is in the OBSERVATION for the LOINC code 59574-4 and inside its OBSERVATION.data is an ELEMENT for the LOINC code 59574-4 (note the same LOINC code is used here too).

The bmi element uses a bmi() function to calculate its value:

value: bmi($weight, $height, $default_bmi)
rules:
loinc:
85353-1:
uri: http://loinc.org/85353-1
name: Vital signs, weight, height, head circumference, oxygen saturation and BMI panel
set:
- attribute: content
elements:
weight_observation:
attribute: data
elements:
weight: # We only need the existing value for the bmi().
height_observation:
attribute: data
elements:
height: # We only need the existing value for the bmi().
bmi_observation:
attribute: data
elements:
bmi:
value: bmi($weight, $height, $default_bmi)
interpretation_interval:
low: 18.5
high: 24.9
vars:
# Normal Distribution values for BMI:
# Source: https://www.scirp.org/journal/paperinformation?paperid=117728
# Default mean in randomNormalDistribution() is an average of male and female means.
default_bmi: randomNormalDistribution((27.6863+25.4960)/2, sqrt(18.65))
define:
weight_observation: http://loinc.org/29463-7 # Same LOINC code is used for OBSERVATION and ELEMENT.
weight: http://loinc.org/29463-7
height_observation: http://loinc.org/8302-2 # Same LOINC code is used for OBSERVATION and ELEMENT.
height: http://loinc.org/8302-2
bmi_observation: http://loinc.org/59574-4
bmi: http://loinc.org/59574-4

Examples of using the bmi() function:

  1. Without defaults:

    value: bmi($weight, $height)
  2. Use variance directly in randomNormalDistribution():

    value: 'bmi($weight, $height, randomNormalDistribution((27.6863+25.4960)/2, (4.4351+4.2031)/2))'
  3. Using (unnecessary) long way to specify an expression:

    value: |
    var weight = $weight;
    var height = $height;
    if (weight == null || height == null) {
    return randomNormalDistribution((27.6863+25.4960)/2, sqrt(18.65));
    } else {
    return bmi(weight, height);
    }

Use programming language code

Programming Language Code

Notice the use of a full programming language in the last example. YAML is great but healthcare data is too complex for YAML. It is good to be able to use code when needed.


Example files

The above examples use the following FHIR source file and produce the openEHR composition.

FHIR source file

{
"resourceType": "Observation",
"id": "blood-pressure",
"meta": {
"profile": [
"http://hl7.org/fhir/StructureDefinition/vitalsigns"
]
},
"text": {
"status": "generated"
},
"identifier": [
{
"system": "urn:ietf:rfc:3986",
"value": "urn:uuid:187e0c12-8dd2-67e2-99b2-bf273c878281"
}
],
"basedOn": [
{
"identifier": {
"system": "https://acme.org/identifiers",
"value": "1234"
}
}
],
"status": "final",
"category": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/observation-category",
"code": "vital-signs",
"display": "Vital Signs"
}
]
}
],
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "85354-9",
"display": "Blood pressure panel with all children optional"
}
],
"text": "Blood pressure systolic & diastolic"
},
"subject": {
"reference": "Patient/example"
},
"effectiveDateTime": "2012-09-17",
"performer": [
{
"reference": "Practitioner/example"
}
],
"interpretation": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code": "L",
"display": "low"
}
],
"text": "Below low normal"
}
],
"bodySite": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "368209003",
"display": "Right arm"
}
]
},
"component": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8480-6",
"display": "Systolic blood pressure"
},
{
"system": "http://snomed.info/sct",
"code": "271649006",
"display": "Systolic blood pressure"
},
{
"system": "http://acme.org/devices/clinical-codes",
"code": "bp-s",
"display": "Systolic Blood pressure"
}
]
},
"valueQuantity": {
"value": 107,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]"
},
"interpretation": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code": "N",
"display": "normal"
}
],
"text": "Normal"
}
]
},
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "8462-4",
"display": "Diastolic blood pressure"
}
]
},
"valueQuantity": {
"value": 60,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]"
},
"interpretation": [
{
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
"code": "L",
"display": "low"
}
],
"text": "Below low normal"
}
]
}
]
}

openEHR composition

{
"_type": "COMPOSITION",
"uid": {
"_type": "OBJECT_VERSION_ID",
"value": "2e3718ca-daac-4836-82cd-4ee1067a7688"
},
"archetype_node_id": "id1",
"name": {
"_type": "DV_TEXT",
"value": "Encounter"
},
"archetype_details": {
"_type": "ARCHETYPED",
"archetype_id": {
"_type": "ARCHETYPE_ID",
"value": "openEHR-EHR-COMPOSITION.encounter.v1"
},
"template_id": {
"_type": "TEMPLATE_ID",
"value": "openEHR-EHR-COMPOSITION.simple-encounter-blood-pressure.v0.0.1"
},
"rm_version": "1.1.0"
},
"language": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "ISO_639-1"
},
"code_string": "en"
},
"territory": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "iso_3166-1-alpha2"
},
"code_string": "US"
},
"category": {
"_type": "DV_CODED_TEXT",
"value": "persistent",
"defining_code": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "iso_3166-1-alpha2"
},
"code_string": "US"
}
},
"composer": {
"_type": "PARTY_IDENTIFIED",
"name": "Lorena Grimes",
"identifiers": [
{
"_type": "DV_IDENTIFIER",
"id": "2e3718ca-daac-4836-82cd-4ee1067a7688"
}
]
},
"context": {
"_type": "EVENT_CONTEXT",
"start_time": {
"_type": "DV_DATE_TIME",
"value": "2012-09-17"
}
},
"content": [
{
"_type": "OBSERVATION",
"archetype_node_id": "openEHR-EHR-OBSERVATION.blood_pressure.v2",
"name": {
"_type": "DV_TEXT",
"value": "blood_pressure"
},
"archetype_details": {
"_type": "ARCHETYPED",
"archetype_id": {
"_type": "ARCHETYPE_ID",
"value": "openEHR-EHR-OBSERVATION.blood_pressure.v2"
},
"rm_version": "1.1.0"
},
"language": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "ISO_639-1"
},
"code_string": "en"
},
"subject": {
"_type": "PARTY_IDENTIFIED",
"name": "Lorena Grimes"
},
"protocol": {
"_type": "ITEM_TREE",
"archetype_node_id": "id12",
"name": {
"_type": "DV_TEXT",
"value": "Tree"
},
"items": [
{
"_type": "ELEMENT",
"archetype_node_id": "id15",
"name": {
"_type": "DV_TEXT",
"value": "Location of measurement"
},
"value": {
"_type": "DV_CODED_TEXT",
"value": "Right arm",
"defining_code": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "SNOMED-CT"
},
"code_string": "368209003"
}
}
}
]
},
"data": {
"_type": "HISTORY",
"archetype_node_id": "id2",
"name": {
"_type": "DV_TEXT",
"value": "History"
},
"origin": {
"_type": "DV_DATE_TIME",
"value": "2012-09-17"
},
"events": [
{
"_type": "POINT_EVENT",
"archetype_node_id": "id7",
"name": {
"_type": "DV_TEXT",
"value": "Any event"
},
"time": {
"_type": "DV_DATE_TIME",
"value": "2012-09-17"
},
"data": {
"_type": "ITEM_TREE",
"archetype_node_id": "id4",
"name": {
"_type": "DV_TEXT",
"value": "Data"
},
"items": [
{
"_type": "ELEMENT",
"archetype_node_id": "id5",
"name": {
"_type": "DV_TEXT",
"value": "Systolic"
},
"value": {
"_type": "DV_QUANTITY",
"magnitude": 107.0,
"property": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "SNOMED-CT"
},
"code_string": "http://snomed.info/id/259018001"
},
"units": "mm[Hg]",
"precision": 0
}
},
{
"_type": "ELEMENT",
"archetype_node_id": "id6",
"name": {
"_type": "DV_TEXT",
"value": "Diastolic"
},
"value": {
"_type": "DV_QUANTITY",
"magnitude": 60.0,
"property": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "SNOMED-CT"
},
"code_string": "http://snomed.info/id/259018001"
},
"units": "mm[Hg]",
"precision": 0
}
},
{
"_type": "ELEMENT",
"archetype_node_id": "id1060",
"name": {
"_type": "DV_TEXT",
"value": "Clinical interpretation"
},
"value": {
"_type": "DV_CODED_TEXT",
"value": "Below low normal",
"defining_code": {
"_type": "CODE_PHRASE",
"terminology_id": {
"_type": "TERMINOLOGY_ID",
"value": "http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation"
},
"code_string": "L"
}
}
}
]
}
}
]
}
}
]
}